Lecture 06: Trees, Network Diagrams and Genomic Representations
0.1.0 An overview of Advanced Graphics and Data Visualization in
R
“Advanced Graphics and Data Visualization in R” is
brought to you by the Centre for the Analysis of Genome Evolution &
Function’s (CAGEF) bioinformatics training initiative. This CSB1021 was
developed to enhance the skills of students with basic backgrounds in R
by focusing on available philosophies, methods, and packages for
plotting scientific data. While the datasets and examples used in this
course will be centred on SARS-CoV-2 epidemiological and genomic data,
the lessons learned herein will be broadly applicable.
This lesson is the sixth and final lecture in a 6-part series. The
aim for the end of this series is for students to recognize how to
import, format, and display data based on their intended message and
audience. The format and style of these visualizations will help to
identify and convey the key message(s) from their experimental data.
The structure of the class is a code-along style in
R markdown notebooks. At the start of each lecture, skeleton versions of
the lecture will be provided for use on the University of Toronto datatools
Hub so students can program along with the instructor.
0.2.0 Lecture objectives
This week will focus on commonly used visualizations related to
genomic information. When working with sequencing data, you may commonly
wish to compare sequences based on their relationships or relative
similarity (trees), by their sequence identity in gene families or
potential interactions (graphs), and or more directly their sequence
motifs.
At the end of this lecture you will have covered the following
topics
- Phylogenetic Trees
- Network graphs
- Genome sequences and markers
0.3.0 A legend for text format in R markdown
grey background - a package, function, code, command or
directory. Backticks are also use for in-line code.
italics - an important term or concept or an individual file or
folder
bold - heading or a term that is being defined
blue text - named or unnamed
hyperlink
... - Within each coding cell this will indicate an area
of code that students will need to complete for the code cell to run
correctly.
Blue box: A key concept that is being introduced
Yellow box: Risk or caution
Green boxes: Recommended reads and resources to
learn R
Red boxes: A comprehension question which may or may
not involve a coding cell. You usually find these at the end of a
section.
0.4.0 Lecture and data files used in this course
0.4.1 Weekly Lecture and skeleton files
Each week, new lesson files will appear within your RStudio folders.
We are pulling from a GitHub repository using this Repository
git-pull link. Simply click on the link and it will take you to the
University of Toronto datatools
Hub. You will need to use your UTORid credentials to complete the
login process. From there you will find each week’s lecture files in the
directory /2024-03-Adv_Graphics_R/Lecture_XX. You will find
a partially coded skeleton.Rmd file as well as all of the
data files necessary to run the week’s lecture.
Alternatively, you can download the R-Markdown Notebook
(.Rmd) and data files from the RStudio server to your
personal computer if you would like to run independently of the Toronto
tools.
0.4.2 Live-coding HTML page
A live lecture version will be available at camok.github.io
that will update as the lecture progresses. Be sure to refresh to take a
look if you get lost!
0.4.3 Post-lecture PDFs
As mentioned above, at the end of each lecture there will be a
completed version of the lecture code released as a PDF file under the
Modules section of Quercus.
0.4.4 Data used in this lesson
Today’s datasets will focus on SARS-CoV-2 variant surveillance data
from the which has been tracking published sequenced genomes for the
appearance of new strains in North America.
0.4.4.1 Dataset 1:
nextstrain_ncov_north-america_canada_timetree.nwk
This is a Newick format data set describing a phylogenetic tree of
SARS-CoV-2 strain information.
0.5.0 Packages used in this lesson
tidyverse which has a number of packages including
dplyr, tidyr, stringr,
forcats and ggplot2
viridis helps to create color-blind palettes for our
data visualizations
RColorBrewer has some hlepful palettes that we’ll need
to colour our data.
ggnewscale will be helpful in generating multiple colour
palettes across increasingly complex plots.
treeio, tidytree, and ggtree
will be used to help import, parse and plot phylogenetic trees.
tidygraph and ggraph will be used to
generate network graph objects and plot them.
ggseqlogo is used for generating sequence logos related
to sequence motifs.
qqman is a wrapper package for generating Manhattan
plots.
lubridate and zoo help us to work with some
date-based information.
# When do we start installing the packages
Sys.time()
# New bioconductor packages we haven't worked with before
if (!requireNamespace("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install("ggnewscale", update = FALSE)
BiocManager::install("ggtree", update = FALSE)
BiocManager::install("ggtreeExtra", update = FALSE)
# New CRAN packages we haven't worked with before
install.packages("tidytree")
install.packages("ggseqlogo")
install.packages("qqman")
install.packages("ggraph")
install.packages("tidygraph")
# When do we finish?
Sys.time()
# Packages to help tidy our data
library(tidyverse)
library(readxl)
# Packages for the graphical analysis section
library(viridis)
library(RColorBrewer)
library(ggnewscale)
# Packages for today's lecture about trees
library(tidytree)
library(ggtree)
library(ggtreeExtra)
library(treeio)
# Packages for graphs
library(ggraph)
library(tidygraph)
# Packages for sequence logos
library(ggseqlogo)
# Packages for Manhattan plots
library(qqman)
# Date calculation helpers
library(lubridate)
library(zoo)
1.0.0 What’s in a name? A phylogenetic tree is just a rebranded
dendrogram
Dendrograms, cladograms, and phylogenetic trees all share a similar
structure with some modifications. All are represented in branching tree
structures that are used to represent the relationships among the
leaves, also known as tips. Branches along the tree may
also be referred to as edges.
Leaves can represent different species, strains, sequences or
multi-dimensional values. Leaves are connected by branches to their
nearest neighbours or relatives. As you move backwards along the tree,
you encounter internal nodes which can connect directly
to more tips or more nodes. The distance between tips and nodes on a
phylogenetic tree, represent a relative distance that may be defined as
some type of evolutionary distance, time, or simple euclidean distance.
In a cladogram, however, distances along branches have no meaning and
only define the presence of a relationship.
In tree terminology, it is helpful to define a few more terms:
| Root |
A tree is rooted when it defines a common ancestor for
all tips in the tree. This could be considered the start or entry point
for the tree |
| Most recent common ancester
(MRCA) |
This is an internal node that collectively joins two or
more tips. |
| Clade |
A group of taxa (tips) that includes a common ancestor
and all of its descendants. |
| Scaling |
This indicates that the branch lengths do
represent a distance metric, whereas, an unscaled tree may have
even-length branches not representative of evolutionary/relationship
distances. |
1.1.0 Where does tree data come from?
As we saw last week, clustering analysis such as that from
hclust() can create dendrogram data. We plotted this in a
couple of ways by itself using fviz_dend() from the
factoextra package and with Heatmap() from the
ComplexHeatmap package. In the case of our first foray, we
were looking at the “relationships” between samples by indicating how
similar they were based on the characteristics in their various
features.
Depending on the software used, trees can be represented in a number
of formats, some of which are described below.
| Newick |
The standard used to represent trees in a
computer-readable format. Trees are encoded in a parentheses-closure
format where each tip takes the form of
“taxa:branch-length” |
|
and pairs of tips are separated by a comma
, and enclosed by parentheses (). Internal
nodes branch lengths are defined outside the parentheses with
“():branch-length” |
| NEXUS/phylip |
Incorporates the Newick tree with separate related data
that is partitioned into different blocks. Blocks are started with
“BEGIN <BLOCK NAME>;” and closed with
“END;” |
| NHX |
New Hampshire eXtended format is also based on Newick
format but instead of code blocks uses a tagging system for each node
extending the format to
“taxa:branch-length[&&NHX:<tag
information>]” |
1.2.0 Using read.tree() from the treeio
package
When opening tree files, depending on the format, you can use the
treeio package and one of it’s many parsing
functions but the simplest way to avoid figuring out how to open a
tree file, is to just use the read.tree() function. This
will determine the appropriate file type and parse through the many
different tree formats before depositing the data into a
phylo object.
Today we’ll be working with a dataset from the Auspice
COVID-19 North American dataset maintained by Finlay Maguire. Unfortunately
this dataset is no longer available but we have an older copy with a
fair amount of metadata.
Let’s begin by opening up the tree data and some related metadata
before taking a look under the hood.
# Import our Newick Tree
SC2_variants_time.phylo <- read.tree("./data/nextstrain_ncov_north-america_canada_timetree.nwk")
# Import our metadata
SC2_metadata.df <- read_tsv("data/nextstrain_ncov_north-america_canada_metadata.tsv")
Rows: 6627 Columns: 24── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (21): Strain, GISAID clade, Nextstrain clade, Clade, Country, Admin Division, Admin Division of exposure, gisaid_epi_isl, Host, Originating...
dbl (1): Age
lgl (1): url
date (1): Collection Data
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# What does the tree look like?
str(SC2_variants_time.phylo)
List of 6
$ edge : int [1:12776, 1:2] 6628 6628 6629 6630 6631 6630 6632 6632 6633 6629 ...
$ edge.length: num [1:12776] 0.0757 0.0848 0.0336 0.0501 0 ...
$ Nnode : int 6150
$ node.label : chr [1:6150] "NODE_0008606" "NODE_0000001" "NODE_0000061" "USA/MA1/2020_travel_history" ...
$ tip.label : chr [1:6627] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" ...
$ root.edge : num 2020
- attr(*, "class")= chr "phylo"
- attr(*, "order")= chr "cladewise"
SC2_variants_time.phylo
Phylogenetic tree with 6627 tips and 6150 internal nodes.
Tip labels:
Wuhan/WH01/2019, USA/MA1/2020, Canada/ON_ON-VIDO-01-2/2020, Canada/ON_VIDO-01/2020, USA/CA-CDC-5/2020, Taiwan/CGMH-CGU-02/2020, ...
Node labels:
NODE_0008606, NODE_0000001, NODE_0000061, USA/MA1/2020_travel_history, NODE_0000062, Canada/ON_VIDO-01/2020_travel_history, ...
Rooted; includes branch lengths.
1.2.1 The phylo object is a simple list
From the looks of our structure, after reading in our Newick file, we
have a list with six elements. They are pretty clearly named with edge
information (a matrix of paired numbers), edge lengths, a count of the
number of nodes (ie internal points where branches bifurcate.
While mostly just numbers, some of these node.label values
appear to be based on travel history information. All of the 6,627
tip.label values correspond to 6,627 SARS-CoV-2 strain
names found in the metadata file as well.
1.3.0 Updating our tree with external information
As you can see our SC2_variants_time.tb object is a
simple data frame now represented as a 4-column table with 12,777 rows.
The edge data is encoded between the parent and
node columns with branch.length to define the
length of each edge. We also have all of the tip and node names stored
under the label column. With the tree in this format, we
can now treat the tree like a tibble and join additional information to
the tree.
Rules to keep in mind:
Don’t lose any node information from your original tree
structure. Losing “observations” is essentially losing edges of your
tree!
Try to work with a complete dataset of information although
sometimes it doesn’t make sense that you would have it all.
Convert your tibble back to a tree format when you’re done with
as.treedata()
You may have information about the leaves or tips of your tree but by
default there is no real internal node information when it comes to
sample origins or lineage. When joining information to the tables, use
the correct direction *_join() to avoid data loss. A
full_join() is probably the safest.
Let’s take a quick look at our metadata and identify what we’re
interested in.
# Look at the metadata, which bits of information would be nice to add?
str(SC2_metadata.df, give.attr = FALSE)
spc_tbl_ [6,627 × 24] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
$ Strain : chr [1:6627] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" ...
$ GISAID clade : chr [1:6627] "L" "L" "L" "L" ...
$ Nextstrain clade : chr [1:6627] "19A" "19A" "19A" "19A" ...
$ Age : num [1:6627] 44 21 NA NA 54 NA 47 47 23 55 ...
$ Clade : chr [1:6627] "19A" "19A" "19A" "19A" ...
$ Country : chr [1:6627] "Asia" "USA" "Canada" "Canada" ...
$ Admin Division : chr [1:6627] "Asia" "Massachusetts" "Ontario" "Ontario" ...
$ Admin Division of exposure: chr [1:6627] "Asia" "Asia" "Ontario" "Asia" ...
$ gisaid_epi_isl : chr [1:6627] "EPI_ISL_406798" "EPI_ISL_409067" "EPI_ISL_425177" "EPI_ISL_413015" ...
$ Host : chr [1:6627] "Human" "Human" "Human" "Human" ...
$ Originating Lab : chr [1:6627] "General Hospital of Central Theater Command of People's Liberation Army of China" "Massachusetts Department of Public Health" "Public Health Ontario" "Public Health Ontario Laboratory" ...
$ PANGO lineage : chr [1:6627] "B" "B" "B" "B" ...
$ Submission Date : chr [1:6627] "Older" "Older" "Older" "Older" ...
$ Region : chr [1:6627] "Asia" "North America" "North America" "North America" ...
$ Region of exposure : chr [1:6627] "Asia" "Asia" "North America" "Asia" ...
$ Sex : chr [1:6627] "Male" "Male" "Male" "Male" ...
$ strain : chr [1:6627] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" ...
$ Emerging Nextstrain clade : chr [1:6627] "19A" "19A" "19A" "19A" ...
$ Submitting Lab : chr [1:6627] "BGI & Institute of Microbiology, Chinese Academy of Sciences & Shandong First Medical University & Shandong Aca"| __truncated__ "Pathogen Discovery, Respiratory Viruses Branch, Division of Viral Diseases, Centers for Disease Control and Prevention" "Public Health Agency of Canada - National Microbiology Laboratory" "National Microbiology Laboratory" ...
$ url : logi [1:6627] NA NA NA NA NA NA ...
$ Collection Data : Date[1:6627], format: "2019-12-26" "2020-01-29" "2020-01-23" "2020-01-23" ...
$ Author : chr [1:6627] "Weijun Chen et al (https://dx.doi.org/10.1016/S0140-6736(20)30251-8)" "Clinton R. Paden et al (https://dx.doi.org/10.1101/2020.03.09.20032896)" "Amrit S. Boese et al" "Shari Tyson et al" ...
$ Country of exposure : chr [1:6627] NA "Asia" NA "Asia" ...
$ Location : chr [1:6627] NA "Boston" NA NA ...
# Check out some of the values for these variables
# Metadata information on the samples
unique(SC2_metadata.df$Country)
[1] "Asia" "USA" "Canada" "Europe" "Guatemala" "Panama"
[7] "Africa" "Dominican Republic" "Costa Rica" "South America" "Oceania" "Jamaica"
[13] "Mexico" "Barbados" "Guadeloupe" "Saint Martin" "Bermuda" "Sint Maarten"
[19] "Saint Barthélemy" "Belize" "Saint Lucia" "El Salvador" "Cuba"
unique(SC2_metadata.df$Age)
[1] 44 21 NA 54 47 23 55 62 49 50 43 25 26 33 38 9 73 64 57 58 0 28 70 71 20 93 36 37 40 56 89 45 34 53 94
[36] 95 86 84 60 90 27 30 59 41 46 52 51 32 24 35 29 61 79 66 78 11 2 65 75 31 48 74 68 67 63 77 42 22 12 5
[71] 17 80 16 39 82 8 7 88 76 69 19 87 3 14 18 1 81 15 6 10 85 4 98 91 72 99 13 92 83 96 104
# Metadata information on the genomes
unique(SC2_metadata.df$'GISAID clade')
[1] "L" "S" "O" "V" "GR" "G" "GV" "GH" "GRY" NA
unique(SC2_metadata.df$'Nextstrain clade')
[1] "19A" "19B" NA "20A" "20B" "20E (EU1)" "20A.EU2" "20C" "20G" "20H/501Y.V2"
[11] "20D" "20J/501Y.V3" "20F" "20I/501Y.V1"
unique(SC2_metadata.df$Clade)
[1] "19A" "19B" "20A" "20E (EU1)" "20C" "20G" "20H/501Y.V2" "20B" "20D" "20J/501Y.V3"
[11] "20F" "20I/501Y.V1"
unique(SC2_metadata.df$'PANGO lineage')
[1] "B" "A" "A.6" "A.2" "A.2.4" "A.2.5" "A.5" "A.1" "A.21" "A.18"
[11] "A.12" "A.28" "A.19" "A.25" "A.23" "A.23.1" "B.12" "B.56" "B.4.7" "B.31"
[21] "B.61" "B.40" "B.35" "B.28" "B.11" "B.55" "B.27" "B.3" "B.4" "B.4.8"
[31] "B.4.2" "B.53" "B.6" "B.1" "B.6.6" "B.6.1" "B.6.2" "B.6.8" "B.1.256" "B.1.1"
[41] "B.1.1.420" "B.1.240" "B.1.205" "B.1.128" "B.1.221" "B.1.223" "B.1.214.2" "B.1.214" "B.1.146" "B.1.147"
[51] "B.1.104" "B.1.391" "B.1.380" "B.1.203" "B.1.540" "B.1.416" "B.1.600" "B.1.160" "B.1.195" "B.1.609"
[61] "B.1.561" "B.1.232" "B.1.558" "B.1.239" "B.1.237" "B.1.597" "B.1.480" "B.1.397" "B.1.408" "B.1.243"
[71] "B.1.240.2" "B.1.240.1" "B.1.524" "B.1.179" "B.1.1.298" "B.1.400" "B.1.398" "B.1.236" "B.1.234" "B.1.459"
[81] "B.1.131" "B.1.525" "B.1.563" "B.1.411" "B.1.379" "B.1.177.31" "B.1.177" "B.1.177.73" "B.1.177.11" "B.1.177.14"
[91] "B.1.177.15" "AA.1" "B.1.177.44" "B.1.177.39" "B.1.177.40" "B.1.177.35" "B.1.177.82" "B.1.177.87" "B.1.177.75" "B.1.177.45"
[101] "B.1.177.28" "B.1.177.32" "B.1.177.37" "B.1.177.72" "B.1.117.2" "B.1.177.4" "B.1.177.53" "W.2" "W.1" "B.1.177.50"
[111] "B.1.177.81" "B.1.177.58" "B.1.177.55" "B.1.177.56" "B.1.177.57" "B.1.177.51" "B.1.177.52" "B.1.177.21" "B.1.177.77" "B.1.177.6"
[121] "B.1.177.7" "B.1.177.86" "B.1.177.8" "B.1.177.62" "B.1.177.60" "U.3" "B.1.177.79" "B.1.9.5" "B.1.9" "B.1.439"
[131] "B.1.111" "B.1.162" "B.1.438" "B.1.164" "B.1.36" "B.1.441" "B.1.36.35" "B.1.468" "B.1.462" "B.1.471"
[141] "B.1.469" "B.1.466" "B.1.466.2" "B.1.470" "B.1.160.16" "B.1.456" "B.1.36.29" "B.1.36.31" "B.1.36.1" "B.1.36.8"
[151] "B.1.36.26" "B.1.36.34" "B.1.36.7" "B.1.36.16" "B.1.36.36" "B.1.36.38" "B.1.36.37" "B.1.36.18" "B.1.36.10" "B.1.476"
[161] "B.1.281" "B.1.273" "B.1.544" "B.1.270" "B.1.277" "B.1.279" "B.1.314" "B.1.367" "B.1.370" "B.1.413"
[171] "B.1.316" "B.1.3" "B.1.509" "B.1.504" "B.1.362" "B.1.499" "B.1.615" "B.1.611" "B.1.505" "B.1.422"
[181] "B.1.596.1" "B.1.362.2" "B.1.369" "B.1.564" "B.1.564.1" "B.1.369.1" "B.1.1.1" "B.1.428" "B.1.356" "B.1.601"
[191] "B.1.562" "B.1.360" "B.1.311" "B.1.598" "B.1.350" "B.1.426" "B.1.265" "B.1.336" "B.1.589" "B.1.567"
[201] "B.1.588.1" "B.1.595" "B.1.596" "B.1.2" "B.1.349" "B.1.366" "B.1.576" "B.1.603" "B.1.526.2" "B.1.526"
[211] "B.1.291" "B.1.575" "B.1.428.3" "B.1.428.1" "B.1.428.2" "B.1.427" "B.1.429" "B.1.495" "B.1.517" "B.1.324"
[221] "B.1.298" "B.1.573" "B.1.320" "B.1.361" "B.1.582" "B.1.612" "B.1.351" "B.1.1.255" "B.1.1.430" "B.1.1.306"
[231] "AE.7" "AE.2" "AE.5" "AE.6" "AE.4" "AE.8" "B.1.153" "B.1.329" "B.1.535" "B.1.313"
[241] "B.1.170" "B.1.22.1" "B.1.201" "B.1.227" "B.1.415" "B.1.94" "B.1.1.519" "B.1.1.222" "B.1.552" "B.1.199"
[251] "B.1.178" "B.1.220" "B.1.6" "B.1.84" "B.1.1.170" "B.1.93" "B.1.91" "B.1.219" "B.1.211" "B.1.388"
[261] "B.1.210" "B.1.555" "B.1.206" "B.1.260" "B.1.420" "B.1.192" "B.1.258" "B.1.258.17" "B.1.242" "B.1.126"
[271] "B.1.378" "B.1.565" "B.1.139" "B.1.8" "B.1.78" "B.1.396" "B.1.560" "B.1.527" "B.1.142" "B.1.393"
[281] "B.1.187" "B.1.149" "B.1.549" "B.1.545" "B.1.189" "B.1.528" "B.1.23" "B.1.67" "B.1.547" "P.2"
[291] "B.1.1.28" "B.1.390" "B.1.395" "B.1.530" "C.35" "B.1.233" "B.1.543" "B.6.7" "B.1.1.369" "B.1.1.372"
[301] "C.8" "C.36" "C.36.1" "C.16" "C.23" "C.1.1" "B.1.1.375" "C.2.1" "C.12" "B.1.1.161"
[311] "B.1.1.87" "B.1.1.192" "B.1.1.71" "B.1.1.10" "L.3" "L.1" "B.1.1.176" "B.1.1.398" "B.1.1.417" "B.1.1.63"
[321] "B.1.1.422" "B.1.1.487" "B.1.1.312" "B.1.1.331" "B.1.1.58" "B.1.1.277" "K.1" "B.1.1.30" "B.1.1.516" "B.1.1.50"
[331] "B.1.1.33" "N.2" "N.3" "N.7" "N.5" "N.8" "N.4" "B.1.1.409" "B.1.1.61" "B.1.1.220"
[341] "R.1" "B.1.1.317" "B.1.1.464.1" "B.1.1.316" "B.1.1.254" "B.1.1.267" "B.1.1.241" "B.1.1.25" "B.1.1.181" "B.1.1.434"
[351] "B.1.1.243" "B.1.1.159" "B.1.1.411" "B.1.1.232" "B.1.1.459" "B.1.1.111" "B.1.1.294" "B.1.1.373" "B.1.1.413" "B.1.1.354"
[361] "B.1.1.54" "B.1.1.325" "B.1.1.399" "B.1.1.46" "B.1.1.274" "B.1.1.410" "B.1.1.315" "AD.2" "B.1.1.142" "B.1.1.485"
[371] "B.1.1.200" "B.1.1.339" "AL.1" "B.1.1.163" "B.1.1.378" "B.1.1.39" "B.1.1.440" "B.1.1.297" "B.1.1.397" "B.1.1.207"
[381] "B.1.1.391" "B.1.1.348" "B.1.1.121" "B.1.1.512" "B.1.1.136" "B.1.1.288" "B.1.1.368" "B.1.1.328" "B.1.1.324" "B.1.1.360"
[391] "B.1.1.359" "B.1.1.374" "B.1.1.12" "B.1.1.407" "B.1.1.500" "B.1.1.432" "B.1.301" "B.1.1.37" "B.1.1.396" "B.1.1.330"
[401] "B.1.1.280" "B.1.1.141" "B.1.1.273" "B.1.1.514" "B.1.1.311" "B.1.1.377" "B.1.1.351" "B.1.1.350" "B.1.1.266" "B.1.1.225"
[411] "B.1.1.371" "B.1.1.438" "B.1.1.153" "B.1.1.134" "B.1.1.439" "B.1.1.152" "B.1.1.275" "B.1.406" "B.1.1.261" "P.1"
[421] "B.1.1.263" "B.1.1.349" "B.1.1.214" "B.1.326" "B.1.1.269" "D.2" "B.1.1.381" "B.1.1.448" "B.1.1.388" "B.1.1.157"
[431] "B.1.1.394" "B.1.1.218" "B.1.1.27" "B.1.1.70" "B.1.1.204" "B.1.1.301" "B.1.1.326" "B.1.1.120" "B.1.1.389" "B.1.1.401"
[441] "B.1.1.404" "B.1.1.416" "B.1.1.216" "AM.1" "AM.4" "B.1.1.34" "B.1.1.198" "B.1.1.319" "B.1.1.7" NA
1.3.1 Pick the tip attributes that are most interesting or
relevant
For our purposes it looks like we will want to explore our data
with:
GISAID clade: A global
initiative on sharing
avian influenza data
scientific consortium of clade naming.
Nextstrain clade: Computationally labeled clade information based
on the Nextstrain criteria and
analysis of new and continuing strains from COVID genomic data.
Emerging Nextstrain clade.
PANGO lineage: a dynamic nomenclature of SARS-CoV-2
lineages.
Country: The country where the sample was collected.
Admin Division: geographic location of the particular
strain/case.
Age: the age of the patients from which the sample was collected
(if included)
Collection Data: the date the data was published or
collected.
Strain: the name of the strain which we’ll need for merging with
our tree structure.
Let’s pull that information down and add it to our
tbl_tree before converting it to a treedata
object.
# Add phylogenetic information to our tree
SC2_variants_time.tree <-
# Pass along the metadata
SC2_metadata.df %>%
# Select just a handful of attributes to go to the tree
select(Strain, 'GISAID clade', 'Emerging Nextstrain clade',
'PANGO lineage', Country, 'Admin Division', 'Collection Data', Age) %>%
rename(strain = Strain,
GISAID = 'GISAID clade',
emerging_nextstrain = 'Emerging Nextstrain clade',
PANGO = 'PANGO lineage',
strain_country = Country,
strain_division = 'Admin Division',
strain_date = 'Collection Data') %>%
# full_join to our tree to ensure no data is lost although you could also carefully use a left_join
full_join(x=SC2_variants_time.tb, y=., by=c("label" = "strain")) %>%
# Convert to a tidytree format
as.treedata()
# Look at the resulting structure
str(SC2_variants_time.tree)
Formal class 'treedata' [package "tidytree"] with 11 slots
..@ file : chr(0)
..@ treetext : chr(0)
..@ phylo :List of 5
.. ..$ edge : int [1:12776, 1:2] 6628 6631 6632 6633 6636 6635 6638 6638 6639 6640 ...
.. .. ..- attr(*, "dimnames")=List of 2
.. .. .. ..$ : NULL
.. .. .. ..$ : chr [1:2] "parent" "node"
.. ..$ edge.length: num [1:12776] 0.0757 0 0.0077 0 0 ...
.. ..$ tip.label : chr [1:6627] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" ...
.. ..$ node.label : chr [1:6150] "NODE_0008606" "NODE_0000001" "NODE_0000061" "USA/MA1/2020_travel_history" ...
.. ..$ Nnode : int 6150
.. ..- attr(*, "class")= chr "phylo"
..@ data : tibble [12,777 × 8] (S3: tbl_df/tbl/data.frame)
.. ..$ node : int [1:12777] 1 2 3 4 5 6 7 8 9 10 ...
.. ..$ GISAID : chr [1:12777] "L" "L" "L" "L" ...
.. ..$ emerging_nextstrain: chr [1:12777] "19A" "19A" "19A" "19A" ...
.. ..$ PANGO : chr [1:12777] "B" "B" "B" "B" ...
.. ..$ strain_country : chr [1:12777] "Asia" "USA" "Canada" "Canada" ...
.. ..$ strain_division : chr [1:12777] "Asia" "Massachusetts" "Ontario" "Ontario" ...
.. ..$ strain_date : Date[1:12777], format: "2019-12-26" "2020-01-29" "2020-01-23" "2020-01-23" ...
.. ..$ Age : num [1:12777] 44 21 NA NA 54 NA 47 47 23 55 ...
..@ extraInfo : tibble [0 × 0] (S3: tbl_df/tbl/data.frame)
Named list()
..@ tip_seq : NULL
..@ anc_seq : NULL
..@ seq_type : chr(0)
..@ tipseq_file: chr(0)
..@ ancseq_file: chr(0)
..@ info : list()
1.3.1.1 How to access S4 object types
Take a quick look at the structure of this treedata object. What do
we see? The treedata class has converted our tibble back
into an S4 object with 11 slots. Each slot, is
essentially a placeholder for another object. We can access these slots
using the @ operator and we can further access sub elements
with the $ operator.
For instance, we can see there is a phylo slot which
looks suspiciously like our original
SC2_variants_time.phylo object. The rest of our data frame
information, which we just added to the tbl_tree before
converting it is stored in the data slot. There are
additional slots where we can add sequencing information for comparison
in different types of phylogenetic tree visualizations.
# Grab our phylo object
SC2_variants_time.tree@phylo
Phylogenetic tree with 6627 tips and 6150 internal nodes.
Tip labels:
Wuhan/WH01/2019, USA/MA1/2020, Canada/ON_ON-VIDO-01-2/2020, Canada/ON_VIDO-01/2020, USA/CA-CDC-5/2020, Taiwan/CGMH-CGU-02/2020, ...
Node labels:
NODE_0008606, NODE_0000001, NODE_0000061, USA/MA1/2020_travel_history, NODE_0000062, Canada/ON_VIDO-01/2020_travel_history, ...
Unrooted; includes branch lengths.
# Take a peek at our associated node data
head(SC2_variants_time.tree@data)
1.4.0 Plot your tree with ggtree() from the
ggtree package
Now that we’ve put some extra data into our tree, we are ready to
plot it with the help of the ggtree() function from the
ggtree package. If you haven’t noticed yet,
treeio, tidytree and ggtree form
a suite of packages that we can use to import, alter, and visualize our
trees.
Parameters for ggtree() include:
tr: the phylo tree object
layout: the shape of the tree including:
rectangular, dendrogram, slanted, ellipse, fan, circular,
inward_circular, and radial.
mrsd: most recent sampling date used for setting a
time-based scale
as.Date: logical to specify if date will be in
calendar vs decimal-date format
branch.length: variable for scaling branch
length
root.position: the position of our root node
(default = 0)
You’ll notice that ggtree() appears to act very much
like the ggplot() command. We’ll be able to add layers to
the base visualization much like we do with other ggplot objects. We’ll
also use theme_tree2() which will automatically add an
x-axis scale to our tree.
We’ll start simple by just visualizing all 6,627 tips of our
tree.
# Show the entire tree
SC2_variants_time.tree %>%
# 1. Data
ggtree(tr = .) +
# 2. Aesthetics
aes(color = emerging_nextstrain) +
# Themes
theme_tree2() +
theme(text = element_text(size = 20),
legend.position = "bottom") + # Move our legend to the bottom
# Provide some labels
labs(x="Time",
title = "Canada and US sequenced strain phylogeny") +
# Make our guide lines a little thicker
guides(colour = guide_legend(title = "Nextstrain\nclade",
override.aes = list(size=2)))
! The tree contained negative edge lengths. If you want to ignore the edges, you can set `options(ignore.negative.edge=TRUE)`, then re-run ggtree.

1.5.0 Filter your tree using drop.tip()
Looking at our visualization, there are a lot of tips to display and
we’ve coloured the tree along the branches. Another thing we can see is
that there is an “NA” value. This mostly represents the branches of the
tree connecting nodes internally since they do not have a specific
lineage associated from our data.
As it stands, there’s just way too much data here to look at and work
with. We can trim that data using the drop.tip() function
without worrying about how the tree is parsed. Internally, all that
pruning will take place within the function. To do this, we’ll generate
a list of tips we want to remove first.
# You can make a list of tips to drop when you're making your ggtree
# Generate a table of tree information to drop
to_drop <-
SC2_metadata.df %>%
# Filter for strains that are not from Canada and are from before 2020-11-01
filter(!(Country == "Canada" &
`Collection Data` >= as.Date("2020-11-01")
)) %>%
# Just keep the strain information
pull(Strain)
# Take a look at what we got back
str(to_drop)
chr [1:6199] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" "USA/CA-CDC-5/2020" ...
# At the same time make a dataset to keep. You'll use this to update the information in the tree later
to_keep <- SC2_metadata.df %>% filter(!Strain %in% to_drop)
# Which tips are we keeping?
str(to_keep, give.attr = FALSE)
spc_tbl_ [428 × 24] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
$ Strain : chr [1:428] "Canada/NS-NML-16223/2021" "Canada/NB-NML-3294/2021" "Canada/NS-NML-16211/2021" "Canada/NB-NML-5584/2021" ...
$ GISAID clade : chr [1:428] "S" "S" "S" "S" ...
$ Nextstrain clade : chr [1:428] "19B" "19B" "19B" "19B" ...
$ Age : num [1:428] 23 25 35 29 61 79 NA NA NA NA ...
$ Clade : chr [1:428] "19B" "19B" "19B" "19B" ...
$ Country : chr [1:428] "Canada" "Canada" "Canada" "Canada" ...
$ Admin Division : chr [1:428] "Nova Scotia" "New Brunswick" "Nova Scotia" "New Brunswick" ...
$ Admin Division of exposure: chr [1:428] "Nova Scotia" "New Brunswick" "Nova Scotia" "New Brunswick" ...
$ gisaid_epi_isl : chr [1:428] "EPI_ISL_1055741" "EPI_ISL_961659" "EPI_ISL_1055730" "EPI_ISL_961643" ...
$ Host : chr [1:428] "Human" "Human" "Human" "Human" ...
$ Originating Lab : chr [1:428] "QEII Health Sciences Centre" "Hôpital Georges L. Dumont" "QEII Health Sciences Centre" "Hôpital Georges L. Dumont" ...
$ PANGO lineage : chr [1:428] "A.2.5" "A.23.1" "A.23.1" "A.23.1" ...
$ Submission Date : chr [1:428] "Older" "Older" "Older" "Older" ...
$ Region : chr [1:428] "North America" "North America" "North America" "North America" ...
$ Region of exposure : chr [1:428] "North America" "North America" "North America" "North America" ...
$ Sex : chr [1:428] "Male" "Female" "Female" "Male" ...
$ strain : chr [1:428] "Canada/NS-NML-16223/2021" "Canada/NB-NML-3294/2021" "Canada/NS-NML-16211/2021" "Canada/NB-NML-5584/2021" ...
$ Emerging Nextstrain clade : chr [1:428] "19B" "19B" "19B" "19B" ...
$ Submitting Lab : chr [1:428] "National Microbiology Laboratory (NML)" "National Microbiology Laboratory (NML)" "National Microbiology Laboratory (NML)" "National Microbiology Laboratory (NML)" ...
$ url : logi [1:428] NA NA NA NA NA NA ...
$ Collection Data : Date[1:428], format: "2021-01-16" "2021-01-02" "2021-01-11" "2021-01-10" ...
$ Author : chr [1:428] "Anna Majer et al" "Anna Majer et al" "Anna Majer et al" "Anna Majer et al" ...
$ Country of exposure : chr [1:428] NA NA NA NA ...
$ Location : chr [1:428] NA NA NA NA ...
1.6.0 Update our pruned tree and add some extra geom_*
layers
The ggtree package brings a number of
ggplot2-compatible geoms to our finger-tips. We’ll
spruce up our tree with some tip labels to begin with. We’ll accomplish
this with the geom_tiplab() layer.
| Tree |
Geom |
geom_tree |
tree structure layer, with multiple layout
supported |
| Tree |
Geom |
geom_treescale |
tree branch scale legend |
| Node |
Geom |
geom_nodepoint |
annotate internal nodes with symbolic points |
| Node |
Annotation |
geom_range |
bar layer to present uncertainty of evolutionary
inference |
| Node |
Annotation |
geom_rootpoint |
annotate root node with symbolic point |
| Branch |
Annotation |
geom_label2 |
modified version of geom_label, with subsetting
supported for labelling branches |
| Branch |
Annotation |
geom_segment2 |
modified version of geom_segment, with subsetting
supported |
| Taxa |
Geom |
geom_point2 |
modified version of geom_point, with subsetting
supported |
| Taxa |
Geom |
geom_tippoint |
annotate external nodes with symbolic points |
| Taxa |
Annotation |
geom_taxalink |
associate two related taxa by linking them with a
curve |
| Taxa |
Annotation |
geom_text2 |
modified version of geom_text, with subsetting
supported |
| Taxa |
Annotation |
geom_tiplab |
layer of tip labels |
| Taxa |
Annotation |
geom_tiplab2 |
layer of tip labels for circular layout |
| Clade |
Annotation |
geom_balance |
highlights the two direct descendant clades of an
internal node |
| Clade |
Annotation |
geom_cladelabel |
annotate a clade with bar and text label |
| Clade |
Annotation |
geom_hilight |
highlight a clade with rectangle |
| Clade |
Annotation |
geom_strip |
annotate associated taxa with bar and (optional) text
label |
# Show our pruned tree
SC2_variants_time.tree %>%
# drop tips based on our list
drop.tip(., to_drop) %>%
# 1. Data
ggtree(tr = .) + # Need the most recent sampling date
# 2. Aesthetics
aes(color = emerging_nextstrain) +
# Themes
theme_tree2() +
theme(text = element_text(size = 20),
legend.position = "bottom") + # Move our legend to the bottom
# Provide some labels
labs(x="Time",
title = "Subset of Canadian sequenced strains") +
# Make our guide lines a little thicker
guides(colour = guide_legend(title = "Nextstrain\nclade",
override.aes = list(size=2))) +
# 4. Geoms
### 1.6.0 Add tips labels to our tree tips
geom_tiplab()

1.6.1 Replace labels with points using different geoms
In a tree this packed, you can’t really read the tip labels. The data
that’s most helpful, however, is the colouring. You can replace your
labels with points in 3 ways:
geom_point()
geom_nodepoint()
geom_tippoint()
If we use geom_point() we can colour/annotate all of the
nodes and tips but we already know that there isn’t any phylogenetic
information associated with our internal nodes. Likewise,
geom_nodepoint() will allow us to colour the nodes but
there isn’t any grouping information associated with that.
Therefore we’ll use geom_tippoint() to colour our tips
based on their Nextstrain clade information and we can alter the tip
shape to match the province where the strain was sequenced.
# Show entire tree
SC2_variants_time.tree %>%
drop.tip(., to_drop) %>%
# 1. Data
ggtree(tr = .) + # Need the most recent sampling date
# 2. Aesthetics
aes(color = emerging_nextstrain) +
# Themes
theme_tree2() +
theme(text = element_text(size = 20),
legend.position = "bottom", # Move our legend to the bottom
legend.box = "vertical") + # Stack legends vertically
# Provide some labels
labs(x="Time",
title = "Subset of Canadian sequenced strains") +
# Make our guide lines a little thicker
guides(colour = guide_legend(title = "Nextstrain\nclade",
override.aes = list(size=2)),
shape = guide_legend(title = "Strain\nlocation")
) +
# 3. Scaling
scale_shape_manual(values = c(15:20, 11)) +
# 4. Geoms
# Add tips labels to our tree tips
### 1.6.1 Change our tip shape by strain_division
geom_tippoint(aes(shape = strain_division), alpha = 0.7, size = 3) # Add tips only

1.6.2 Fix your x-axis with the mrsd parameter
As you can see we are working with a time-based axis but it all
appears to be on a relative scale and what we’d really like to see is
real-world time. In order to do that, we can assign the
mrsd parameter to the most recent sampling date in our
dataset. We’ll also set our parameter as.Date in order to
see the date in a calendar format rather than a decimal-date format.
# Show entire tree
SC2_variants_time.tree %>%
drop.tip(., to_drop) %>%
# 1. Data
ggtree(tr = .,
mrsd = "2021-02-17", ### 1.6.2 Set the most recent sampling date
as.Date = TRUE) + ### 1.6.2 Make sure it's set as a data
# 2. Aesthetics
aes(color = emerging_nextstrain) +
# Themes
theme_tree2() +
theme(text = element_text(size = 20),
legend.position = "bottom", # Move our legend to the bottom
legend.box = "vertical") + # Stack legends vertically
# Provide some labels
labs(x="Time",
title = "Subset of Canadian sequenced strains") +
# Make our guide lines a little thicker
guides(colour = guide_legend(title = "Nextstrain\nclade",
override.aes = list(size=2)),
shape = guide_legend(title = "Strain\nlocation")
) +
# 3. Scaling
scale_shape_manual(values = c(15:20, 11)) +
# 4. Geoms
# Add tips labels to our tree tips
# Change our tip shape by strain_division
geom_tippoint(aes(shape = strain_division), alpha = 0.7, size = 3) # Add tips only

1.7.0 Thin out the tree some more with viewClade() and
other information functions
Looking at the tree there are still quite a few nodes but we can zoom
in on a specific clade using the viewClade() function. To
accomplish this we need to access the most recent common ancestor (MRCA)
of a group. At the same time, we should talk about ways to traverse or
navigate this tree. Here’s where tidytree offers up a few
additional functions for searching your tbl_tree. These
take on the form of function(tbl_tree, node_number) or
function(tbl_tree, node_label)
| child() |
Find the children of an internal node. |
| parent() |
Find the parent node of a tip or other node. |
| offspring() |
Find all of the offspring of a node. |
| ancestor() |
Find all of the ancestors of a tip or node |
| sibling() |
Find the nearest sibling of a tip (or node) |
| MRCA() |
Find the most recent common ancestor between two or
more nodes |
Let’s filter our taxa list a little more searching for variants of
the clade 501Y.V1 and 501Y.V2. Then we’ll find the MRCA between those
tips.
# You can make a list of tips to drop when you're making your ggtree
# Generate a table of tree information to drop
to_view <-
SC2_metadata.df %>%
# Filter our tip information
filter(`Emerging Nextstrain clade` %in% c("20I/501Y.V1", "20H/501Y.V2") &
Country == "Canada" &
`Collection Data` >= as.Date("2021-02-16")
) %>%
# Just keep the strain names
pull(Strain)
# What's the list look like?
to_view
[1] "Canada/NL-NML-17170/2021" "Canada/NL-NML-17182/2021" "Canada/NL-NML-17173/2021" "Canada/NL-NML-17193/2021" "Canada/NL-NML-17157/2021"
[6] "Canada/NL-NML-17185/2021" "Canada/NL-NML-17189/2021" "Canada/NL-NML-17196/2021" "Canada/NL-NML-17184/2021" "Canada/NL-NML-17166/2021"
[11] "Canada/NL-NML-17191/2021" "Canada/NL-NML-17172/2021" "Canada/NL-NML-17187/2021" "Canada/NL-NML-17168/2021" "Canada/NL-NML-17194/2021"
[16] "Canada/NL-NML-17158/2021" "Canada/NL-NML-17183/2021" "Canada/NL-NML-17165/2021"
# Calculate the MRCA from a subset of our tips
to_view_MRCA <-
# Pass along our tree information
SC2_variants_time.tree %>%
# Get rid of the tips we would before (to match the tree we'll plot)
drop.tip(., to_drop) %>%
# Determine the MRCA between the first 4 tips in the list
MRCA(., to_view[1:4])
# What kind of value does MRCA() return?
to_view_MRCA
[1] 727
# How many offspring does it have?
# Pass along our tree information
SC2_variants_time.tree %>%
# Drop our original set of tips
drop.tip(., to_drop) %>%
# make a tibble so we can see all the relevant information
as_tibble() %>%
# Find the offspring of a specific node
offspring(., to_view_MRCA) %>%
# Convert to a tree
as.treedata() %>%
str()
Formal class 'treedata' [package "tidytree"] with 11 slots
..@ file : chr(0)
..@ treetext : chr(0)
..@ phylo :List of 5
.. ..$ edge : int [1:71, 1:2] 728 730 731 732 732 734 735 735 736 737 ...
.. .. ..- attr(*, "dimnames")=List of 2
.. .. .. ..$ : NULL
.. .. .. ..$ : chr [1:2] "parent" "node"
.. ..$ edge.length: num [1:71] 0.1136 0.067 0.0521 0.0306 0.0333 ...
.. ..$ tip.label : chr [1:39] "Canada/NL-NML-16819/2021" "Canada/NL-NML-16824/2021" "Canada/NL-NML-17178/2021" "Canada/NL-NML-16829/2021" ...
.. ..$ node.label : chr [1:32] "NODE_0006538" "NODE_0006539" "NODE_0006540" "NODE_0006541" ...
.. ..$ Nnode : int 32
.. ..- attr(*, "class")= chr "phylo"
..@ data : tibble [71 × 8] (S3: tbl_df/tbl/data.frame)
.. ..$ node : int [1:71] 323 324 325 326 327 328 329 330 331 332 ...
.. ..$ GISAID : chr [1:71] "GRY" "GRY" "GRY" "GRY" ...
.. ..$ emerging_nextstrain: chr [1:71] "20I/501Y.V1" "20I/501Y.V1" "20I/501Y.V1" "20I/501Y.V1" ...
.. ..$ PANGO : chr [1:71] "B.1.1.7" "B.1.1.7" "B.1.1.7" "B.1.1.7" ...
.. ..$ strain_country : chr [1:71] "Canada" "Canada" "Canada" "Canada" ...
.. ..$ strain_division : chr [1:71] "Newfoundland and Labrador" "Newfoundland and Labrador" "Newfoundland and Labrador" "Newfoundland and Labrador" ...
.. ..$ strain_date : Date[1:71], format: "2021-02-10" "2021-02-12" "2021-02-15" "2021-02-12" ...
.. ..$ Age : num [1:71] 47 16 26 62 60 45 19 45 35 31 ...
..@ extraInfo : tibble [0 × 0] (S3: tbl_df/tbl/data.frame)
Named list()
..@ tip_seq : NULL
..@ anc_seq : NULL
..@ seq_type : chr(0)
..@ tipseq_file: chr(0)
..@ ancseq_file: chr(0)
..@ info : list()
1.7.1 Pass a ggplot2 object to
viewClade()
We see that our chosen MRCA node (727) has 39 children based on the
number of tip labels in our treedata object. We can also
now use this MRCA to determine the specific clade we can see on the tree
but this will still be quite large. To view this particular clade, we
build our plot as before, and then zoom into it afterwards with the
viewClade() function.
Unfortunately, we’ll have to ditch our date scale and revert to a
decimal-date. If you’re not using a calendar-based date, it’s not a
problem at all. If we look carefully at the data, we’ll see that the
internal nodes have an NA-value. If you were industrious
enough, you could use the branch lengths to traverse the tree and
calculate dates for all the internal nodes as well. This would likely
solve the issue.
Let’s drop the tip names back in there too.
# Show a small clade of the tree
tree.plot <-
SC2_variants_time.tree %>%
# From our tree, drop the Non-Canadian tips
drop.tip(., to_drop) %>%
# 1. Data
ggtree(tr = ., mrsd = "2021-02-16") + # Need the most recent sampling date
# 2. Aesthetics
aes(color = emerging_nextstrain) +
# Themes
theme_tree2() +
theme(text = element_text(size = 30),
legend.position = "bottom", # Move our legend to the bottom
legend.box = "vertical") + # Stack legends vertically
# Provide some labels
labs(x="Time",
title = "Subset of Canadian sequenced strains") +
# Make our guide lines a little thicker
guides(colour = guide_legend(title = "Nextstrain\nclade",
override.aes = list(size=2)),
shape = guide_legend(title = "Strain\nlocation")
) +
# 3. Scaling
scale_shape_manual(values = c(15:20, 11)) +
# 4. Geoms
geom_tiplab(size = 7, align=TRUE) + # Add labels in and right-align them
# Add tips labels to our tree tips
# Change our tip shape by strain_division
geom_tippoint(aes(shape = strain_division), size = 3) # Add tips only
### 1.7.1 View the clade we made using the same MRCA call we already used.
viewClade(tree.plot, MRCA(tree.plot, to_view[1:5]))

1.8.0 Make a new subplot with information from across all
clades
So we’ve looked at a few ways to subset and plot our phylogenetic
tree. We’ll do a quick aside to create a more curated list of tips with
representation from across multiple clades. We’ll use this as the base
tree for our next few graphs so that we can really see the power of
using additional information from our treedata object.
# Here's our representative list of tips from multiple clades
curated_tips <-
c('Wuhan/WH01/2019',
'Canada/ON_ON-VIDO-01-2/2020',
'Canada/ON_VIDO-01/2020',
'Canada/BC_37_0-2/2020',
'Canada/BC_69243/2020',
'Canada/QC-CHUM-2008003956A/2020',
'Canada/NL-NML-1387/2020',
'Canada/MB-NML-808/2020',
'Canada/NB-NML-16967/2021',
'Canada/BC_6129127/2020',
'Canada/AB-97776/2020',
'Canada/AB-12948/2020',
'Canada/BC-BCCDC-3564/2020',
'Canada/ON-S67/2020',
'Canada/AB-65233/2020',
'Canada/MB-NML-1057/2020',
'Canada/ON-UHTC-0366/2020',
'Canada/NS-NML-16218/2021',
'Canada/NB-NML-16966/2021',
'Canada/NS_13/2020',
'Canada/Qc-CHUM-2019203856A/2020',
'Canada/NB-NML-3272/2020',
'Canada/ON-UHTC-0267/2020',
'Canada/MB-NML-1037/2020',
'Canada/NS-NML-5213/2020',
'Canada/NS-NML-5141/2021',
'Canada/NS-NML-5140/2021',
'Canada/NS-NML-16208/2021',
'Canada/BC-BCCDC-7012/2020',
'Canada/ON-LTRI-1372/2020',
'Canada/BC-BCCDC-9736/2021',
'Canada/ON-S2125/2021',
'Canada/NS-NML-5181/2020',
'Canada/ON-PPS-21012021_0269/2021',
'Canada/QC-L00314539/2020',
'Canada/NS-NML-16238/2021',
'Canada/NL-NML-16822/2021',
'Canada/QC-L00324589001/2021',
'Canada/NL-NML-17194/2021')
# Make a new list of tips to drop
curated_drop_list <-
SC2_metadata.df %>%
# filter the opposite of our tip labels
filter(!(Strain %in% curated_tips)) %>%
# Only keep the strain information for what we want to drop
pull(Strain)
# Drop tips from our tree and save it as curated_tree_info.df
curated_tree_info.df <-
# Pass along our original tree
SC2_variants_time.tree %>%
# Drop the tips we created
drop.tip(., ...) %>%
# Convert to a tibble
as_tibble() %>%
# Rename our column information
rename(Clade = ..., Province = ...) %>%
mutate(Age = replace_na(Age, replace = 0)) %>%
# Convert this to a data frame
as.data.frame()
# Set the rownames from the label information
rownames(curated_tree_info.df) <- curated_tree_info.df$label
# Let's view the tree
str(curated_tree_info.df)
1.9.0 Convert your tree style with the layout
parameter
So we’ve only been working with a rectangular tree (horizontal
layout) thus far but as we mentioned at the beginning there are actually
a number of different layouts we can use. Our goal now is to make a sort
of sunburst plot using a circular layout that we’ll add visual
categorical information to in a ring pattern.
First, we’ll start with the circular version. We’ll colour our tips
by the province where the case or data was generated and label them by
the Nextstrain clade information.
# Show our curated tree in a circular format
tree.plot <-
SC2_variants_time.tree %>%
drop.tip(., curated_drop_list) %>%
# 1. Data
ggtree(., layout = ..., ### 1.9.0 Set the layout to circular
mrsd = "2021-02-17") + # Need the most recent sampling date
# 2. Aesthetics
# Themes
theme(text = element_text(size = 20),
legend.position = "bottom") +
labs(title = "Canada and US sequenced strain phylogeny",
colour = "Province") +
scale_colour_viridis_d(option = "plasma") + # This seems to literally kill the kernel!
# 4. Geoms
geom_tippoint(aes(colour = strain_division), size = 5) +
### 1.9.0 set the tip labels
geom_tiplab(aes(label = ...), size = 8)
# View our tree
tree.plot
1.10.0 Use gheatmap() layer to add information to your
plot
Now that we have our basic circular tree, we can use the information
from curated_tree_info.df to visualize additional data on
our tree. In order to use the gheatmap layer properly, we
must pass it our original tree plot, along with a new dataframe (or
vector) that holds the information we want to add. It should use the
rownames from the data frame to help match up to the tips in our
tree.
We’ll drop the tip labels from our tree in favour of the heatmap
we’ll add. By adding layers of hierarchical data, this creates what is
known as a Sunburst plot.
The gheatmap() layer takes some of the following parameters:
p: the tree plot we want to modify.
data: the data used to modify the tree plot
p.
offset: determines the offset of the heatmap
relative to the tree.
width: determines the total width of the heatmaps
compared to the tree
colnames_angle: determines the angle of the text for
the heatmap.
font.size: sets the font size for the heatmap
portion of the tree.
# Show our curated tree in a circular format
tree.plot <-
SC2_variants_time.tree %>%
drop.tip(., curated_drop_list) %>%
# 1. Data
ggtree(., layout = "circular", mrsd = "2021-02-17") + # Need the most recent sampling date
# 2. Aesthetics
# Themes
theme(text = element_text(size = 20),
legend.position = "bottom") +
labs(title = "Canada and US sequenced strain phylogeny",
colour = "Province") +
scale_colour_viridis_d(option = "plasma") +
# 4. Geoms
geom_tippoint(aes(colour = strain_division), size = 5) # Add tips only
### 1.10.0 Add a circular heatmap with gheatmap()
tree.plot <- ...(tree.plot, ...,
offset = 0.1, width = 0.1,
colnames_angle = 90, font.size = 6, hjust = 1) +
scale_fill_continuous(name = "Tree coding")
tree.plot
1.10.1 ggnewscale package allows multiple color/fill
scales on your Sunburst plot
From our sunburst plot above we used multiple columns from
curated_tree_info.df but the caveat is that the legend for
each resulting column combined all of the data into a single new legend.
That’s helpful if the type of data could be continuous values
like a [0,1] scale heatmap colour across different features. When
dealing with multiple categories where the scale of the values varies,
however, that doesn’t work well for us. This also doesn’t work well for
categorical data.
Instead, we’d like to separate the colour/fill scales by repeatedly
adding to our plot, layer by layer. As you might recall, however, we
also run into the problem of scale_colour_* and
scale_fill_* layers overwriting any previous
layers.
To circumvent this reality, we’ll use the ggnewscale
package to generate new colour and fill scales with
new_scale_fill() and new_scale_colour(). It
can make the process slightly more encumbering but it will work out.
We’ll add back in our original tip labels as well for this graph.
# Show our curated tree in a circular format
# We'll need some extra colour sets to accomplish this plot
combo.colours = c(brewer.pal(12, "Paired"), brewer.pal(8, "Set1"), brewer.pal(12, "Set3"))
# Save our first plot with just the tree
tree.plot <-
SC2_variants_time.tree %>%
drop.tip(., curated_drop_list) %>%
# 1. Data
ggtree(., layout = "circular", mrsd = "2021-02-17") + # Need the most recent sampling date
# 2. Aesthetics
# Themes
theme(text = element_text(size = 30),
legend.position = "bottom") +
labs(title = "Canada and US sequenced strain phylogeny") +
# We'll want to ensure our colour guide ends up in the correct order
scale_colour_discrete(guide = guide_legend(title="Province", order=1)) +
# 4. Geoms
geom_tippoint(aes(colour = strain_division), size = 5) + # Add tips only
geom_tiplab(size = 10, align=TRUE, offset = 0.6) # Add in our labels
# Generate our first heatmap layer
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(parent, node, branch.length),
offset = 0.1, width = 0.1,
colnames_angle = 90, font.size = 6) +
# Set the name of our legend
scale_fill_continuous(name = "Tree coding",
guide = guide_legend(order = 2))
### 1.10.1 use this code to create a new colour scale
tree.plot <- tree.plot + ...
# Generate a categorical heatmap layer for the Clade variable
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(Clade),
offset = 0.2, width = 0.1,
colnames_angle = 90, font.size = 12) +
# Set the name and order of our Clade legend
scale_fill_discrete(name = "Nextstrain\nClade",
guide = guide_legend(order=3))
### 1.10.1 use this code to create a new colour scale
tree.plot <- tree.plot + ...
# Generate a categorical heatmap layer for the PANGO variable
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(...),
offset = 0.35, width = 0.1,
colnames_angle = 90, font.size = 12) +
# Set the name and order of our PANGO legend
scale_fill_manual(values = combo.colours, name = "PANGO\nlineage",
guide=guide_legend(order=4))
# You may need to re-render to see it properly (Expand/collapse again)
tree.plot
1.10.2 Create a vertical version of our plot using a rectangular
layout
Depending on the level of data depth, you may also choose to layer
your scales along the right-hand side of your tree. To accomplish this
we can simply set our layout parameter to “rectangular”. At
the same time, we’ll run into a couple of problems with how the
ggtree is made.
One issue with ggplot is that as we add the scales, these are very
much like annotations on our ggplot. The vertical (and horizontal)
display areas of our plot are not updated as we add these annotations.
When we add new colour scale layers to the plot, the labels will, in
many cases, bleed outside of the designated plot area. To combat this,
we can add a layer called vexpand() or
hexpand() for horizontal expansion. Both of these take 2
parameters:
ratio: the ratio of expansion for your plot axis
direction: the direction you want the expansion in a
range from 1 (left/top) to -1 (right/bottom).
# Show our curated tree in a rectangular format
# We'll need some extra colour sets to accomplish this plot
combo.colours = c(brewer.pal(12, "Paired"), brewer.pal(8, "Set1"), brewer.pal(12, "Set3"))
# Save our first plot with just the tree
tree.plot <-
SC2_variants_time.tree %>%
drop.tip(., curated_drop_list) %>%
# 1. Data
ggtree(., layout = ..., ### 1.10.2 convert your plot to a rectangular format
mrsd = "2021-02-17") + # Need the most recent sampling date
# 2. Aesthetics
# Themes
theme(text = element_text(size = 30),
legend.position = "bottom",
) +
labs(title = "Canada and US sequenced strain phylogeny",
colour = "Province") +
### 1.10.2 Expand the vertical plot size
vexpand(ratio = ..., direction = ...) +
# We'll want to ensure our colour guide ends up in the correct order
scale_colour_discrete(guide = guide_legend(title="Province", order=1)) +
# 4. Geoms
geom_tippoint(aes(colour = strain_division), size = 5) + # Add tips only
geom_tiplab(size = 10, align=TRUE) # Add in our labels
# Generate our first heatmap layer
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(parent, node, branch.length),
offset = 0.75, width = 0.1,
colnames_angle = 90, font.size = 8, hjust = 1) +
# Set the name of our legend
scale_fill_continuous(name = "Tree coding",
guide = guide_legend(order = 2))
# use this code to create a new colour scale
tree.plot <- tree.plot + new_scale_fill()
# Generate a categorical heatmap layer for the Clade variable
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(Clade),
offset = 0.85, width = 0.1,
colnames_angle = 90, font.size = 12, hjust = 1) +
# Set the name and order of our Clade legend
scale_fill_discrete(name = "Nextstrain\nClade",
guide = guide_legend(order=3))
# use this code to create a new colour scale
tree.plot <- tree.plot + new_scale_fill()
# Generate a categorical heatmap layer for the PANGO variable
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(PANGO),
offset = 1.0, width = 0.1,
colnames_angle = 90, font.size = 12, hjust = 1) +
# Set the name and order of our PANGO legend
scale_fill_manual(values = combo.colours, name = "PANGO\nlineage",
guide=guide_legend(order=4))
# View the tree
tree.plot
1.11.0 Annotate connections between tips with
geom_taxalink()
Suppose we want to join to tips/taxa together to suggest that there
is a non-tree relationship between them or to emphasize some association
between nodes. To connect nodes or tips of the tree, we can use the
geom_taxalink() layer.
Let’s join the nodes Canada/NL-NML-17194/2021 and
Canada/ON-UHTC-0366/2020 together.
# Show our curated tree in a rectangular format
# We'll need some extra colour sets to accomplish this plot
combo.colours = c(brewer.pal(12, "Paired"), brewer.pal(8, "Set1"), brewer.pal(12, "Set3"))
# Save our first plot with just the tree
tree.plot <-
SC2_variants_time.tree %>%
drop.tip(., curated_drop_list) %>%
# 1. Data
ggtree(., layout = "rectangular", mrsd = "2021-02-17") + # Need the most recent sampling date
# 2. Aesthetics
# Themes
theme(text = element_text(size = 30),
legend.position = "bottom",
) +
labs(title = "Canada and US sequenced strain phylogeny",
colour = "Province") +
vexpand(ratio = 0.1, direction = -1) + # Expand the vertical plot size
# We'll want to ensure our colour guide ends up in the correct order
scale_colour_discrete(guide = guide_legend(title="Province", order=1)) +
# 4. Geoms
geom_tippoint(aes(colour = strain_division), size = 5) + # Add tips only
geom_tiplab(size = 10, align=TRUE) + # Add in our labels
### 1.11.0 Add in some annotation between points
geom_taxalink(taxa1=..., taxa2=...,
color="blue", alpha=0.8, offset=0,
outward=FALSE,
arrow=arrow(length=unit(0.01, "npc")))
# Generate our first heatmap layer
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(parent, node, branch.length),
offset = 0.75, width = 0.1,
colnames_angle = 90, font.size = 8, hjust = 1) +
# Set the name of our legend
scale_fill_continuous(name = "Tree coding",
guide = guide_legend(order = 2))
# use this code to create a new colour scale
tree.plot <- tree.plot + new_scale_fill()
# Generate a categorical heatmap layer for the Clade variable
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(Clade),
offset = 0.85, width = 0.1,
colnames_angle = 0, font.size = 10) +
# Set the name and order of our Clade legend
scale_fill_discrete(name = "Nextstrain\nClade",
guide = guide_legend(order=3))
# use this code to create a new colour scale
tree.plot <- tree.plot + new_scale_fill()
# Generate a categorical heatmap layer for the PANGO variable
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(PANGO),
offset = 1.0, width = 0.1,
colnames_angle = 0, font.size = 10) +
# Set the name and order of our PANGO legend
scale_fill_manual(values = combo.colours, name = "PANGO\nlineage",
guide=guide_legend(order=4))
# View the tree
tree.plot
1.12.0 There is definitely more to explore from the
ggtree package
So we’ve spent quite a bit of time looking at phylogenetic trees and
how to add external data to tips and nodes. We’ve barely scratched the
surface and there are a lot of additional functions within the
ggtree package that you can work with to build amazing
plots including adding fasta sequence data through
msaplot(). You can experiment with labeling internal nodes,
and specific clades as well. You can also facet_plot()
rectangular trees with other kinds of plots!
We didn’t even have time to combine all sorts of plot data with our
circular trees using the ggtreeExtra package. Definitely
check out Chapter 10
of the tree book to be inspired by the potential things you could do
with more complex datasets like this:

The geom_fruit() layer can be used to add extra visualization to your
sunburst plot through the ggtreeExtra package
2.0.0 What are network diagrams and how can we use them?
We’ve seen a lot of trees today but a closely related structure is
the network diagram. They share similar concepts in that both have nodes
and branches. However, nodes are called vertices and
can represent almost anything, branches between nodes are
edges and can span across multiple nodes, instead of in
a bifurcating tree relationship. Relationships between vertices can be
bidirectional, and depending on what you’d like to present, multiple
parallel edges may exist. We saw a specific version of network diagrams
in Lecture 04 with our Sankey plots!
Network graphs are great for showing the interconnections between
entities within your data. This kind of visualization can also help us
realize where subtle connections occur (or don’t occur!). Depending on
how you’ve weighted or chosen edges, you can convey how strong
relationships between entities are as well.
To work with graph data and plot it, we’ll be using a couple of
companion packages: tidygraph and ggraph. More
about that later!
Since we already have some metadata about COVID genomes, let’s see if
we can’t convert some of it to a network diagram to better understand
strain information? We’ll begin by selecting some information from our
Nextstrain metadata set.
str(SC2_metadata.df, give.attr = FALSE)
2.1.0 Use the tidygraph package to help prepare
data
Now that we have some information that we want to work with, we want
to convert that kind of data to something that can be interpreted into a
graph. The tidygraph package provides a way to hook graph
data into the tidyverse so that we can use common verbs and
ideas to filter and work with it. Using this package we can convert our
dataframe information into a tbl_graph object which is
actually an igraph object.
The function we’ll use to convert our data is
as_tbl_graph() but it has some expectations about the data.
The parameters of this function are:
x: the data frame we’d like to convert to an
igraph.
nodes: a data frame with our node
information.
edges: a data frame of two columns containing
integers matching node information to describe the relationship between
nodes.
If you have both a nodes and edges data frame you can certainly
generate a table this way. However, we have a complex dataframe and we
want to track all of the information in it. To that end we will
add columns from and to based on
variables that already exist and the graph nodes will be generated from
this information. When we provide the data frame, the function will
recognize the columns present and produce node-based data and generate
edge characteristics from our other columns.
# Now we'll add some specific variables used to generate our graph table
SC2_graph_info.df %>%
# Create our "from" and "to" columns in our data frame
mutate(from = source_location,
to = Country
) %>%
# Filter for just strain data from Canada and Asia
filter(Country %in% c(...)) %>%
# Convert to an igraph
as_tbl_graph() %>%
# Look at the structure of the igraph
str()
2.3.0 There are many more visualizations from
ggraph
Interestingly in the first year of the pandemic, from the sequenced
genome data, infections within Canada appear to originate mostly from
within North America but there are some infections that originated from
Asia, and entered through Ontario and British Columbia - our two main
global ports of entry!
We’ve really only covered linear graph layouts in our data but there
are actually many kinds of graphs that this package can
produce. The layout parameter is the key to exploring all
of the graph types available. Of course your data layout all has to make
sense! You’ll find great examples from data-imaginist.com
like this circlepack graph:

There are all kinds of network graphs that can use additional layers
of metadata to shape and present your data
5.0.0 Class Summary
With this final lecture we’ve covered the spectrum of visualizations
covering the most basic of scatter- and barplots, graduating to
boxplots, violin plots, and their variants. We’ve covered
high-throughput data visualizations including volcano plots, heatmaps,
and principal component analysis. Furthermore we looked at how simple
the projection of high-dimension data can be with t-SNE and UMAP.
We finished our course today covering phylogenetic trees, network
graphs, and some sequence analysis visualizations. Overall you now have
the tools to wrangle data that may appear in all sorts of formats along
with a better understanding of when and how to prepare some of the most
common data visualizations in your burgeoning science careers.
Congratulations and good luck on your data science journey!
5.0.1 Post-course survey
We have created a post-course survey you can fill out anonymously.
You can use this survey as an opportunity to tell us about your
experience and help shape the future offerings of this series. Please
take 5-10 minutes to fill out the survey. We really appreciate your
feedback!
Anonymous Google Survey
found here
5.1.0 Weekly assignment
This week’s assignment will be found under the current lecture folder
under the “assignment” subfolder. It will include an R markdown notebook
that you will use to produce the code and answers for this week’s
assignment. Please provide answers in markdown or code cells that
immediately follow each question section.
| Code |
50% |
- Does it follow best practices? |
|
|
- Does it make good use of available packages? |
|
|
- Was data prepared properly |
| Answers and Output |
50% |
- Is output based on the correct dataset? |
|
|
- Are groupings appropriate |
|
|
- Are correct titles/axes/legends correct? |
|
|
- Is interpretation of the graphs correct? |
Since coding styles and solutions can differ, students are encouraged
to use best practices. Assignments may be rewarded for
well-coded or elegant solutions.
You can save and download the markdown notebook in its native format.
Submit this file to the the appropriate assignment section by 12:59 pm
on the date of our next class: April 25th, 2024.
5.2.0 Acknowledgements
Revision 1.0.0: created and prepared for
CSB1021H S LEC0141, 03-2021 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 1.0.1: edited and prepared for
CSB1020H S LEC0141, 03-2022 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 1.0.2: edited and prepared for
CSB1020H S LEC0141, 03-2023 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 2.0.0: Revised and prepared for
CSB1020H S LEC0141, 03-2024 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
The Center for the Analysis of Genome Evolution and Function
(CAGEF)
The Centre for the Analysis of Genome Evolution and Function (CAGEF)
at the University of Toronto offers comprehensive experimental design,
research, and analysis services in microbiome and metagenomic studies,
genomics, proteomics, and bioinformatics.
From targeted DNA amplicon sequencing to transcriptomes, whole
genomes, and metagenomes, from protein identification to
post-translational modification, CAGEF has the tools and knowledge to
support your research. Our state-of-the-art facility and experienced
research staff provide a broad range of services, including both
standard analyses and techniques developed by our team. In particular,
we have special expertise in microbial, plant, and environmental
systems.
For more information about us and the services we offer, please visit
https://www.cagef.utoronto.ca/.
LS0tDQp0aXRsZTogJycNCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCiMgVGhpcyBhbGxvd3MgdGhlIGZpbGUgdG8gYmUgTElWRSBhbmQgcnVuIHdpdGhvdXQgZXJyb3JzIHN0b3BwaW5nIGl0Lg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVycm9yID0gVFJVRSkNCmBgYA0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0NBR0VGX3NlcnZpY2VzX3NsaWRlLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQoNCiMgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSIHRlc3QNCg0KIyBMZWN0dXJlIDA2OiBUcmVlcywgTmV0d29yayBEaWFncmFtcyBhbmQgR2Vub21pYyBSZXByZXNlbnRhdGlvbnMNCg0KIyMgMC4xLjAgQW4gb3ZlcnZpZXcgb2YgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSDQoNCioqIkFkdmFuY2VkIEdyYXBoaWNzIGFuZCBEYXRhIFZpc3VhbGl6YXRpb24gaW4gUiIqKiBpcyBicm91Z2h0IHRvIHlvdSBieSB0aGUgQ2VudHJlIGZvciB0aGUgQW5hbHlzaXMgb2YgR2Vub21lIEV2b2x1dGlvbiAmIEZ1bmN0aW9uJ3MgKENBR0VGKSBiaW9pbmZvcm1hdGljcyB0cmFpbmluZyBpbml0aWF0aXZlLiBUaGlzIENTQjEwMjEgd2FzIGRldmVsb3BlZCB0byBlbmhhbmNlIHRoZSBza2lsbHMgb2Ygc3R1ZGVudHMgd2l0aCBiYXNpYyBiYWNrZ3JvdW5kcyBpbiBSIGJ5IGZvY3VzaW5nIG9uIGF2YWlsYWJsZSBwaGlsb3NvcGhpZXMsIG1ldGhvZHMsIGFuZCBwYWNrYWdlcyBmb3IgcGxvdHRpbmcgc2NpZW50aWZpYyBkYXRhLiBXaGlsZSB0aGUgZGF0YXNldHMgYW5kIGV4YW1wbGVzIHVzZWQgaW4gdGhpcyBjb3Vyc2Ugd2lsbCBiZSBjZW50cmVkIG9uIFNBUlMtQ29WLTIgZXBpZGVtaW9sb2dpY2FsIGFuZCBnZW5vbWljIGRhdGEsIHRoZSBsZXNzb25zIGxlYXJuZWQgaGVyZWluIHdpbGwgYmUgYnJvYWRseSBhcHBsaWNhYmxlLg0KDQpUaGlzIGxlc3NvbiBpcyB0aGUgc2l4dGggYW5kIGZpbmFsIGxlY3R1cmUgaW4gYSA2LXBhcnQgc2VyaWVzLiBUaGUgYWltIGZvciB0aGUgZW5kIG9mIHRoaXMgc2VyaWVzIGlzIGZvciBzdHVkZW50cyB0byByZWNvZ25pemUgaG93IHRvIGltcG9ydCwgZm9ybWF0LCBhbmQgZGlzcGxheSBkYXRhIGJhc2VkIG9uIHRoZWlyIGludGVuZGVkIG1lc3NhZ2UgYW5kIGF1ZGllbmNlLiBUaGUgZm9ybWF0IGFuZCBzdHlsZSBvZiB0aGVzZSB2aXN1YWxpemF0aW9ucyB3aWxsIGhlbHAgdG8gaWRlbnRpZnkgYW5kIGNvbnZleSB0aGUga2V5IG1lc3NhZ2UocykgZnJvbSB0aGVpciBleHBlcmltZW50YWwgZGF0YS4NCg0KVGhlIHN0cnVjdHVyZSBvZiB0aGUgY2xhc3MgaXMgYSAqKmNvZGUtYWxvbmcgc3R5bGUqKiBpbiBSIG1hcmtkb3duIG5vdGVib29rcy4gQXQgdGhlIHN0YXJ0IG9mIGVhY2ggbGVjdHVyZSwgc2tlbGV0b24gdmVyc2lvbnMgb2YgdGhlIGxlY3R1cmUgd2lsbCBiZSBwcm92aWRlZCBmb3IgdXNlIG9uIHRoZSBbVW5pdmVyc2l0eSBvZiBUb3JvbnRvIGRhdGF0b29scyBIdWJdKGh0dHBzOi8vZGF0YXRvb2xzLnV0b3JvbnRvLmNhKSBzbyBzdHVkZW50cyBjYW4gcHJvZ3JhbSBhbG9uZyB3aXRoIHRoZSBpbnN0cnVjdG9yLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMC4yLjAgTGVjdHVyZSBvYmplY3RpdmVzDQoNClRoaXMgd2VlayB3aWxsIGZvY3VzIG9uIGNvbW1vbmx5IHVzZWQgdmlzdWFsaXphdGlvbnMgcmVsYXRlZCB0byBnZW5vbWljIGluZm9ybWF0aW9uLiBXaGVuIHdvcmtpbmcgd2l0aCBzZXF1ZW5jaW5nIGRhdGEsIHlvdSBtYXkgY29tbW9ubHkgd2lzaCB0byBjb21wYXJlIHNlcXVlbmNlcyBiYXNlZCBvbiB0aGVpciByZWxhdGlvbnNoaXBzIG9yIHJlbGF0aXZlIHNpbWlsYXJpdHkgKHRyZWVzKSwgYnkgdGhlaXIgc2VxdWVuY2UgaWRlbnRpdHkgaW4gZ2VuZSBmYW1pbGllcyBvciBwb3RlbnRpYWwgaW50ZXJhY3Rpb25zIChncmFwaHMpLCBhbmQgb3IgbW9yZSBkaXJlY3RseSB0aGVpciBzZXF1ZW5jZSBtb3RpZnMuDQoNCkF0IHRoZSBlbmQgb2YgdGhpcyBsZWN0dXJlIHlvdSB3aWxsIGhhdmUgY292ZXJlZCB0aGUgZm9sbG93aW5nIHRvcGljcw0KDQoxLiAgUGh5bG9nZW5ldGljIFRyZWVzDQoyLiAgTmV0d29yayBncmFwaHMNCjMuICBHZW5vbWUgc2VxdWVuY2VzIGFuZCBtYXJrZXJzDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAwLjMuMCBBIGxlZ2VuZCBmb3IgdGV4dCBmb3JtYXQgaW4gUiBtYXJrZG93bg0KDQpgZ3JleSBiYWNrZ3JvdW5kYCAtIGEgcGFja2FnZSwgZnVuY3Rpb24sIGNvZGUsIGNvbW1hbmQgb3IgZGlyZWN0b3J5LiBCYWNrdGlja3MgYXJlIGFsc28gdXNlIGZvciBpbi1saW5lIGNvZGUuXA0KKml0YWxpY3MqIC0gYW4gaW1wb3J0YW50IHRlcm0gb3IgY29uY2VwdCBvciBhbiBpbmRpdmlkdWFsIGZpbGUgb3IgZm9sZGVyXA0KKipib2xkKiogLSBoZWFkaW5nIG9yIGEgdGVybSB0aGF0IGlzIGJlaW5nIGRlZmluZWRcDQpbYmx1ZSB0ZXh0XXtzdHlsZT0iY29sb3I6Ymx1ZSJ9IC0gbmFtZWQgb3IgdW5uYW1lZCBoeXBlcmxpbmsNCg0KYC4uLmAgLSBXaXRoaW4gZWFjaCBjb2RpbmcgY2VsbCB0aGlzIHdpbGwgaW5kaWNhdGUgYW4gYXJlYSBvZiBjb2RlIHRoYXQgc3R1ZGVudHMgd2lsbCBuZWVkIHRvIGNvbXBsZXRlIGZvciB0aGUgY29kZSBjZWxsIHRvIHJ1biBjb3JyZWN0bHkuDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtaW5mb30NCioqQmx1ZSBib3g6KiogQSBrZXkgY29uY2VwdCB0aGF0IGlzIGJlaW5nIGludHJvZHVjZWQNCjo6Og0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LXdhcm5pbmd9DQoqKlllbGxvdyBib3g6KiogUmlzayBvciBjYXV0aW9uDQo6OjoNCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1zdWNjZXNzfQ0KKipHcmVlbiBib3hlczoqKiBSZWNvbW1lbmRlZCByZWFkcyBhbmQgcmVzb3VyY2VzIHRvIGxlYXJuIFINCjo6Og0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWRhbmdlcn0NCioqUmVkIGJveGVzOioqIEEgY29tcHJlaGVuc2lvbiBxdWVzdGlvbiB3aGljaCBtYXkgb3IgbWF5IG5vdCBpbnZvbHZlIGEgY29kaW5nIGNlbGwuIFlvdSB1c3VhbGx5IGZpbmQgdGhlc2UgYXQgdGhlIGVuZCBvZiBhIHNlY3Rpb24uDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuNC4wIExlY3R1cmUgYW5kIGRhdGEgZmlsZXMgdXNlZCBpbiB0aGlzIGNvdXJzZQ0KDQojIyMgMC40LjEgV2Vla2x5IExlY3R1cmUgYW5kIHNrZWxldG9uIGZpbGVzDQoNCkVhY2ggd2VlaywgbmV3IGxlc3NvbiBmaWxlcyB3aWxsIGFwcGVhciB3aXRoaW4geW91ciBSU3R1ZGlvIGZvbGRlcnMuIFdlIGFyZSBwdWxsaW5nIGZyb20gYSBHaXRIdWIgcmVwb3NpdG9yeSB1c2luZyB0aGlzIFtSZXBvc2l0b3J5IGdpdC1wdWxsIGxpbmtdKGh0dHBzOi8vci5kYXRhdG9vbHMudXRvcm9udG8uY2EvaHViL3VzZXItcmVkaXJlY3QvZ2l0LXB1bGw/cmVwbz1odHRwcyUzQSUyRiUyRmdpdGh1Yi5jb20lMkZjYW1vayUyRjIwMjQtMDMtQWR2X0dyYXBoaWNzX1ImdXJscGF0aD1yc3R1ZGlvJTJGJmJyYW5jaD1tYWluKS4gU2ltcGx5IGNsaWNrIG9uIHRoZSBsaW5rIGFuZCBpdCB3aWxsIHRha2UgeW91IHRvIHRoZSBbVW5pdmVyc2l0eSBvZiBUb3JvbnRvIGRhdGF0b29scyBIdWJdKGh0dHBzOi8vZGF0YXRvb2xzLnV0b3JvbnRvLmNhKS4gWW91IHdpbGwgbmVlZCB0byB1c2UgeW91ciBVVE9SaWQgY3JlZGVudGlhbHMgdG8gY29tcGxldGUgdGhlIGxvZ2luIHByb2Nlc3MuIEZyb20gdGhlcmUgeW91IHdpbGwgZmluZCBlYWNoIHdlZWsncyBsZWN0dXJlIGZpbGVzIGluIHRoZSBkaXJlY3RvcnkgYC8yMDI0LTAzLUFkdl9HcmFwaGljc19SL0xlY3R1cmVfWFhgLiBZb3Ugd2lsbCBmaW5kIGEgcGFydGlhbGx5IGNvZGVkIGBza2VsZXRvbi5SbWRgIGZpbGUgYXMgd2VsbCBhcyBhbGwgb2YgdGhlIGRhdGEgZmlsZXMgbmVjZXNzYXJ5IHRvIHJ1biB0aGUgd2VlaydzIGxlY3R1cmUuDQoNCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gZG93bmxvYWQgdGhlIFItTWFya2Rvd24gTm90ZWJvb2sgKGAuUm1kYCkgYW5kIGRhdGEgZmlsZXMgZnJvbSB0aGUgUlN0dWRpbyBzZXJ2ZXIgdG8geW91ciBwZXJzb25hbCBjb21wdXRlciBpZiB5b3Ugd291bGQgbGlrZSB0byBydW4gaW5kZXBlbmRlbnRseSBvZiB0aGUgVG9yb250byB0b29scy4NCg0KIyMjIDAuNC4yIExpdmUtY29kaW5nIEhUTUwgcGFnZQ0KDQpBIGxpdmUgbGVjdHVyZSB2ZXJzaW9uIHdpbGwgYmUgYXZhaWxhYmxlIGF0IFtjYW1vay5naXRodWIuaW9dKGh0dHBzOi8vY2Ftb2suZ2l0aHViLmlvLzIwMjQtMDMuQWR2X0dyYXBoaWNzX1IvaW5kZXguaHRtbCkgdGhhdCB3aWxsIHVwZGF0ZSBhcyB0aGUgbGVjdHVyZSBwcm9ncmVzc2VzLiBCZSBzdXJlIHRvIHJlZnJlc2ggdG8gdGFrZSBhIGxvb2sgaWYgeW91IGdldCBsb3N0IQ0KDQojIyMgMC40LjMgUG9zdC1sZWN0dXJlIFBERnMNCg0KQXMgbWVudGlvbmVkIGFib3ZlLCBhdCB0aGUgZW5kIG9mIGVhY2ggbGVjdHVyZSB0aGVyZSB3aWxsIGJlIGEgY29tcGxldGVkIHZlcnNpb24gb2YgdGhlIGxlY3R1cmUgY29kZSByZWxlYXNlZCBhcyBhIFBERiBmaWxlIHVuZGVyIHRoZSBNb2R1bGVzIHNlY3Rpb24gb2YgUXVlcmN1cy4NCg0KIyMgMC40LjQgRGF0YSB1c2VkIGluIHRoaXMgbGVzc29uDQoNClRvZGF5J3MgZGF0YXNldHMgd2lsbCBmb2N1cyBvbiBTQVJTLUNvVi0yIHZhcmlhbnQgc3VydmVpbGxhbmNlIGRhdGEgZnJvbSB0aGUgd2hpY2ggaGFzIGJlZW4gdHJhY2tpbmcgcHVibGlzaGVkIHNlcXVlbmNlZCBnZW5vbWVzIGZvciB0aGUgYXBwZWFyYW5jZSBvZiBuZXcgc3RyYWlucyBpbiBOb3J0aCBBbWVyaWNhLg0KDQojIyMgMC40LjQuMSBEYXRhc2V0IDE6IG5leHRzdHJhaW5fbmNvdl9ub3J0aC1hbWVyaWNhX2NhbmFkYV90aW1ldHJlZS5ud2sNCg0KVGhpcyBpcyBhIE5ld2ljayBmb3JtYXQgZGF0YSBzZXQgZGVzY3JpYmluZyBhIHBoeWxvZ2VuZXRpYyB0cmVlIG9mIFNBUlMtQ29WLTIgc3RyYWluIGluZm9ybWF0aW9uLg0KDQojIyMgMC40LjQuMiBEYXRhc2V0IDI6IG5leHRzdHJhaW5fbmNvdl9ub3J0aC1hbWVyaWNhX2NhbmFkYV9tZXRhZGF0YS50c3YNCg0KTWV0YWRhdGEgdGhhdCBhY2NvbXBhbmllcyB0aGUgZmlyc3QgZGF0YXNldC4gSXQgbGlua3Mgc3RyYWluIGluZm9ybWF0aW9uIGJhY2sgdG8gYXMgbXVjaCBnZW9ncmFwaGljYWwgYW5kIHJlbGF0ZWQgaW5mb3JtYXRpb24gYXMgcG9zc2libGUuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAwLjUuMCBQYWNrYWdlcyB1c2VkIGluIHRoaXMgbGVzc29uDQoNCmB0aWR5dmVyc2VgIHdoaWNoIGhhcyBhIG51bWJlciBvZiBwYWNrYWdlcyBpbmNsdWRpbmcgYGRwbHlyYCwgYHRpZHlyYCwgYHN0cmluZ3JgLCBgZm9yY2F0c2AgYW5kIGBnZ3Bsb3QyYA0KDQpgdmlyaWRpc2AgaGVscHMgdG8gY3JlYXRlIGNvbG9yLWJsaW5kIHBhbGV0dGVzIGZvciBvdXIgZGF0YSB2aXN1YWxpemF0aW9ucw0KDQpgUkNvbG9yQnJld2VyYCBoYXMgc29tZSBobGVwZnVsIHBhbGV0dGVzIHRoYXQgd2UnbGwgbmVlZCB0byBjb2xvdXIgb3VyIGRhdGEuDQoNCmBnZ25ld3NjYWxlYCB3aWxsIGJlIGhlbHBmdWwgaW4gZ2VuZXJhdGluZyBtdWx0aXBsZSBjb2xvdXIgcGFsZXR0ZXMgYWNyb3NzIGluY3JlYXNpbmdseSBjb21wbGV4IHBsb3RzLg0KDQpgdHJlZWlvYCwgYHRpZHl0cmVlYCwgYW5kIGBnZ3RyZWVgIHdpbGwgYmUgdXNlZCB0byBoZWxwIGltcG9ydCwgcGFyc2UgYW5kIHBsb3QgcGh5bG9nZW5ldGljIHRyZWVzLg0KDQpgdGlkeWdyYXBoYCBhbmQgYGdncmFwaGAgd2lsbCBiZSB1c2VkIHRvIGdlbmVyYXRlIG5ldHdvcmsgZ3JhcGggb2JqZWN0cyBhbmQgcGxvdCB0aGVtLg0KDQpgZ2dzZXFsb2dvYCBpcyB1c2VkIGZvciBnZW5lcmF0aW5nIHNlcXVlbmNlIGxvZ29zIHJlbGF0ZWQgdG8gc2VxdWVuY2UgbW90aWZzLg0KDQpgcXFtYW5gIGlzIGEgd3JhcHBlciBwYWNrYWdlIGZvciBnZW5lcmF0aW5nIE1hbmhhdHRhbiBwbG90cy4NCg0KYGx1YnJpZGF0ZWAgYW5kIGB6b29gIGhlbHAgdXMgdG8gd29yayB3aXRoIHNvbWUgZGF0ZS1iYXNlZCBpbmZvcm1hdGlvbi4NCg0KYGBge3J9DQojIFdoZW4gZG8gd2Ugc3RhcnQgaW5zdGFsbGluZyB0aGUgcGFja2FnZXMNClN5cy50aW1lKCkNCg0KIyBOZXcgYmlvY29uZHVjdG9yIHBhY2thZ2VzIHdlIGhhdmVuJ3Qgd29ya2VkIHdpdGggYmVmb3JlDQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQ0KICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikNCg0KQmlvY01hbmFnZXI6Omluc3RhbGwoImdnbmV3c2NhbGUiLCB1cGRhdGUgPSBGQUxTRSkNCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJnZ3RyZWUiLCB1cGRhdGUgPSBGQUxTRSkNCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJnZ3RyZWVFeHRyYSIsIHVwZGF0ZSA9IEZBTFNFKQ0KDQojIE5ldyBDUkFOIHBhY2thZ2VzIHdlIGhhdmVuJ3Qgd29ya2VkIHdpdGggYmVmb3JlDQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dHJlZSIpDQppbnN0YWxsLnBhY2thZ2VzKCJnZ3NlcWxvZ28iKQ0KaW5zdGFsbC5wYWNrYWdlcygicXFtYW4iKQ0KaW5zdGFsbC5wYWNrYWdlcygiZ2dyYXBoIikNCmluc3RhbGwucGFja2FnZXMoInRpZHlncmFwaCIpDQoNCiMgV2hlbiBkbyB3ZSBmaW5pc2g/DQpTeXMudGltZSgpDQpgYGANCg0KYGBge3J9DQojIFBhY2thZ2VzIHRvIGhlbHAgdGlkeSBvdXIgZGF0YQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJlYWR4bCkNCg0KIyBQYWNrYWdlcyBmb3IgdGhlIGdyYXBoaWNhbCBhbmFseXNpcyBzZWN0aW9uDQpsaWJyYXJ5KHZpcmlkaXMpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkoZ2duZXdzY2FsZSkNCg0KIyBQYWNrYWdlcyBmb3IgdG9kYXkncyBsZWN0dXJlIGFib3V0IHRyZWVzDQpsaWJyYXJ5KHRpZHl0cmVlKQ0KbGlicmFyeShnZ3RyZWUpDQpsaWJyYXJ5KGdndHJlZUV4dHJhKQ0KbGlicmFyeSh0cmVlaW8pDQoNCiMgUGFja2FnZXMgZm9yIGdyYXBocw0KbGlicmFyeShnZ3JhcGgpDQpsaWJyYXJ5KHRpZHlncmFwaCkNCg0KIyBQYWNrYWdlcyBmb3Igc2VxdWVuY2UgbG9nb3MNCmxpYnJhcnkoZ2dzZXFsb2dvKQ0KDQojIFBhY2thZ2VzIGZvciBNYW5oYXR0YW4gcGxvdHMNCmxpYnJhcnkocXFtYW4pDQoNCiMgRGF0ZSBjYWxjdWxhdGlvbiBoZWxwZXJzDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoem9vKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDEuMC4wIFdoYXQncyBpbiBhIG5hbWU/IEEgcGh5bG9nZW5ldGljIHRyZWUgaXMganVzdCBhIHJlYnJhbmRlZCBkZW5kcm9ncmFtDQoNCkRlbmRyb2dyYW1zLCBjbGFkb2dyYW1zLCBhbmQgcGh5bG9nZW5ldGljIHRyZWVzIGFsbCBzaGFyZSBhIHNpbWlsYXIgc3RydWN0dXJlIHdpdGggc29tZSBtb2RpZmljYXRpb25zLiBBbGwgYXJlIHJlcHJlc2VudGVkIGluIGJyYW5jaGluZyB0cmVlIHN0cnVjdHVyZXMgdGhhdCBhcmUgdXNlZCB0byByZXByZXNlbnQgdGhlIHJlbGF0aW9uc2hpcHMgYW1vbmcgdGhlIGxlYXZlcywgYWxzbyBrbm93biBhcyAqKnRpcHMqKi4gQnJhbmNoZXMgYWxvbmcgdGhlIHRyZWUgbWF5IGFsc28gYmUgcmVmZXJyZWQgdG8gYXMgKiplZGdlcyoqLg0KDQpMZWF2ZXMgY2FuIHJlcHJlc2VudCBkaWZmZXJlbnQgc3BlY2llcywgc3RyYWlucywgc2VxdWVuY2VzIG9yIG11bHRpLWRpbWVuc2lvbmFsIHZhbHVlcy4gTGVhdmVzIGFyZSBjb25uZWN0ZWQgYnkgYnJhbmNoZXMgdG8gdGhlaXIgbmVhcmVzdCBuZWlnaGJvdXJzIG9yIHJlbGF0aXZlcy4gQXMgeW91IG1vdmUgYmFja3dhcmRzIGFsb25nIHRoZSB0cmVlLCB5b3UgZW5jb3VudGVyIGludGVybmFsICoqbm9kZXMqKiB3aGljaCBjYW4gY29ubmVjdCBkaXJlY3RseSB0byBtb3JlIHRpcHMgb3IgbW9yZSBub2Rlcy4gVGhlIGRpc3RhbmNlIGJldHdlZW4gdGlwcyBhbmQgbm9kZXMgb24gYSBwaHlsb2dlbmV0aWMgdHJlZSwgcmVwcmVzZW50IGEgcmVsYXRpdmUgZGlzdGFuY2UgdGhhdCBtYXkgYmUgZGVmaW5lZCBhcyBzb21lIHR5cGUgb2YgZXZvbHV0aW9uYXJ5IGRpc3RhbmNlLCB0aW1lLCBvciBzaW1wbGUgZXVjbGlkZWFuIGRpc3RhbmNlLiBJbiBhIGNsYWRvZ3JhbSwgaG93ZXZlciwgZGlzdGFuY2VzIGFsb25nIGJyYW5jaGVzIGhhdmUgbm8gbWVhbmluZyBhbmQgb25seSBkZWZpbmUgdGhlIHByZXNlbmNlIG9mIGEgcmVsYXRpb25zaGlwLg0KDQpJbiB0cmVlIHRlcm1pbm9sb2d5LCBpdCBpcyBoZWxwZnVsIHRvIGRlZmluZSBhIGZldyBtb3JlIHRlcm1zOg0KDQp8IFRlcm0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgKipSb290KiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBBIHRyZWUgaXMgcm9vdGVkIHdoZW4gaXQgZGVmaW5lcyBhIGNvbW1vbiBhbmNlc3RvciBmb3IgYWxsIHRpcHMgaW4gdGhlIHRyZWUuIFRoaXMgY291bGQgYmUgY29uc2lkZXJlZCB0aGUgc3RhcnQgb3IgZW50cnkgcG9pbnQgZm9yIHRoZSB0cmVlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgKipNb3N0IHJlY2VudCBjb21tb24gYW5jZXN0ZXIgKE1SQ0EpKiogfCBUaGlzIGlzIGFuIGludGVybmFsIG5vZGUgdGhhdCBjb2xsZWN0aXZlbHkgam9pbnMgdHdvIG9yIG1vcmUgdGlwcy4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgKipDbGFkZSoqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBBIGdyb3VwIG9mIHRheGEgKHRpcHMpIHRoYXQgaW5jbHVkZXMgYSBjb21tb24gYW5jZXN0b3IgYW5kICphbGwqIG9mIGl0cyBkZXNjZW5kYW50cy4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgKipTY2FsaW5nKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBUaGlzIGluZGljYXRlcyB0aGF0IHRoZSBicmFuY2ggbGVuZ3RocyAqZG8qIHJlcHJlc2VudCBhIGRpc3RhbmNlIG1ldHJpYywgd2hlcmVhcywgYW4gdW5zY2FsZWQgdHJlZSBtYXkgaGF2ZSBldmVuLWxlbmd0aCBicmFuY2hlcyBub3QgcmVwcmVzZW50YXRpdmUgb2YgZXZvbHV0aW9uYXJ5L3JlbGF0aW9uc2hpcCBkaXN0YW5jZXMuIHwNCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9QaHlsb2dlbmV0aWMtVHJlZS01NDJ4NDIwLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KDQpEaWFncmFtIG9mIHBoeWxvZ2VuZXRpYyB0cmVlIHRlcm1zIGZyb20gWzEwIGRpZmZlcmVuY2VzIGJldHdlZW4gY2xhZG9ncmFtcyBhbmQgcGh5bG9nZW5ldGljcyB0cmVlc10oaHR0cHM6Ly92aXZhZGlmZmVyZW5jZXMuY29tL2RpZmZlcmVuY2UtYmV0d2Vlbi1jbGFkb2dyYW0tYW5kLXBoeWxvZ2VuZXRpYy10cmVlLykNCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMS4xLjAgV2hlcmUgZG9lcyB0cmVlIGRhdGEgY29tZSBmcm9tPw0KDQpBcyB3ZSBzYXcgbGFzdCB3ZWVrLCBjbHVzdGVyaW5nIGFuYWx5c2lzIHN1Y2ggYXMgdGhhdCBmcm9tIGBoY2x1c3QoKWAgY2FuIGNyZWF0ZSBkZW5kcm9ncmFtIGRhdGEuIFdlIHBsb3R0ZWQgdGhpcyBpbiBhIGNvdXBsZSBvZiB3YXlzIGJ5IGl0c2VsZiB1c2luZyBgZnZpel9kZW5kKClgIGZyb20gdGhlIGBmYWN0b2V4dHJhYCBwYWNrYWdlIGFuZCB3aXRoIGBIZWF0bWFwKClgIGZyb20gdGhlIGBDb21wbGV4SGVhdG1hcGAgcGFja2FnZS4gSW4gdGhlIGNhc2Ugb2Ygb3VyIGZpcnN0IGZvcmF5LCB3ZSB3ZXJlIGxvb2tpbmcgYXQgdGhlICJyZWxhdGlvbnNoaXBzIiBiZXR3ZWVuIHNhbXBsZXMgYnkgaW5kaWNhdGluZyBob3cgc2ltaWxhciB0aGV5IHdlcmUgYmFzZWQgb24gdGhlIGNoYXJhY3RlcmlzdGljcyBpbiB0aGVpciB2YXJpb3VzIGZlYXR1cmVzLg0KDQpEZXBlbmRpbmcgb24gdGhlIHNvZnR3YXJlIHVzZWQsIHRyZWVzIGNhbiBiZSByZXByZXNlbnRlZCBpbiBhIG51bWJlciBvZiBmb3JtYXRzLCBzb21lIG9mIHdoaWNoIGFyZSBkZXNjcmliZWQgYmVsb3cuDQoNCnwgRm9ybWF0ICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgTmV3aWNrICAgICAgIHwgVGhlIHN0YW5kYXJkIHVzZWQgdG8gcmVwcmVzZW50IHRyZWVzIGluIGEgY29tcHV0ZXItcmVhZGFibGUgZm9ybWF0LiBUcmVlcyBhcmUgZW5jb2RlZCBpbiBhIHBhcmVudGhlc2VzLWNsb3N1cmUgZm9ybWF0IHdoZXJlIGVhY2ggdGlwIHRha2VzIHRoZSBmb3JtIG9mICIqKnRheGE6YnJhbmNoLWxlbmd0aCoqIiAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICB8IGFuZCBwYWlycyBvZiB0aXBzIGFyZSBzZXBhcmF0ZWQgYnkgYSBjb21tYSBgLGAgYW5kIGVuY2xvc2VkIGJ5IHBhcmVudGhlc2VzIGAoKWAuIEludGVybmFsIG5vZGVzIGJyYW5jaCBsZW5ndGhzIGFyZSBkZWZpbmVkIG91dHNpZGUgdGhlIHBhcmVudGhlc2VzIHdpdGggIioqKCk6YnJhbmNoLWxlbmd0aCoqIiAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBORVhVUy9waHlsaXAgfCBJbmNvcnBvcmF0ZXMgdGhlIE5ld2ljayB0cmVlIHdpdGggc2VwYXJhdGUgcmVsYXRlZCBkYXRhIHRoYXQgaXMgcGFydGl0aW9uZWQgaW50byBkaWZmZXJlbnQgYmxvY2tzLiBCbG9ja3MgYXJlIHN0YXJ0ZWQgd2l0aCAiKipCRUdJTiBcPEJMT0NLIE5BTUVcPjsqKiIgYW5kIGNsb3NlZCB3aXRoICIqKkVORDsqKiIgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgTkhYICAgICAgICAgIHwgTmV3IEhhbXBzaGlyZSBlWHRlbmRlZCBmb3JtYXQgaXMgYWxzbyBiYXNlZCBvbiBOZXdpY2sgZm9ybWF0IGJ1dCBpbnN0ZWFkIG9mIGNvZGUgYmxvY2tzIHVzZXMgYSB0YWdnaW5nIHN5c3RlbSBmb3IgZWFjaCBub2RlIGV4dGVuZGluZyB0aGUgZm9ybWF0IHRvICIqKnRheGE6YnJhbmNoLWxlbmd0aFsmJk5IWDpcPHRhZyBpbmZvcm1hdGlvblw+XSoqIiB8DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjIuMCBVc2luZyBgcmVhZC50cmVlKClgIGZyb20gdGhlIGB0cmVlaW9gIHBhY2thZ2UNCg0KV2hlbiBvcGVuaW5nIHRyZWUgZmlsZXMsIGRlcGVuZGluZyBvbiB0aGUgZm9ybWF0LCB5b3UgY2FuIHVzZSB0aGUgYHRyZWVpb2AgcGFja2FnZSBhbmQgb25lIG9mIGl0J3MgbWFueSBbcGFyc2luZyBmdW5jdGlvbnNdKGh0dHBzOi8veXVsYWItc211LnRvcC90cmVlZGF0YS1ib29rL2NoYXB0ZXIxLmh0bWwpIGJ1dCB0aGUgc2ltcGxlc3Qgd2F5IHRvIGF2b2lkIGZpZ3VyaW5nIG91dCBob3cgdG8gb3BlbiBhIHRyZWUgZmlsZSwgaXMgdG8ganVzdCB1c2UgdGhlIGByZWFkLnRyZWUoKWAgZnVuY3Rpb24uIFRoaXMgd2lsbCBkZXRlcm1pbmUgdGhlIGFwcHJvcHJpYXRlIGZpbGUgdHlwZSBhbmQgcGFyc2UgdGhyb3VnaCB0aGUgbWFueSBkaWZmZXJlbnQgdHJlZSBmb3JtYXRzIGJlZm9yZSBkZXBvc2l0aW5nIHRoZSBkYXRhIGludG8gYSBgcGh5bG9gIG9iamVjdC4NCg0KVG9kYXkgd2UnbGwgYmUgd29ya2luZyB3aXRoIGEgZGF0YXNldCBmcm9tIHRoZSBbQXVzcGljZSBDT1ZJRC0xOSBOb3J0aCBBbWVyaWNhbiBkYXRhc2V0XShodHRwOi8vYXVzcGljZS5maW5sYXltYWd1aS5yZS9uY292L25vcnRoLWFtZXJpY2EvY2FuYWRhL29udGFyaW8pIG1haW50YWluZWQgYnkgW0ZpbmxheSBNYWd1aXJlXShodHRwczovL2ZpbmxheW1hZ3VpLnJlLykuICoqVW5mb3J0dW5hdGVseSB0aGlzIGRhdGFzZXQgaXMgbm8gbG9uZ2VyIGF2YWlsYWJsZSBidXQgd2UgaGF2ZSBhbiBvbGRlciBjb3B5IHdpdGggYSBmYWlyIGFtb3VudCBvZiBtZXRhZGF0YS4qKg0KDQpMZXQncyBiZWdpbiBieSBvcGVuaW5nIHVwIHRoZSB0cmVlIGRhdGEgYW5kIHNvbWUgcmVsYXRlZCBtZXRhZGF0YSBiZWZvcmUgdGFraW5nIGEgbG9vayB1bmRlciB0aGUgaG9vZC4NCg0KYGBge3J9DQojIEltcG9ydCBvdXIgTmV3aWNrIFRyZWUNClNDMl92YXJpYW50c190aW1lLnBoeWxvIDwtIHJlYWQudHJlZSgiLi9kYXRhL25leHRzdHJhaW5fbmNvdl9ub3J0aC1hbWVyaWNhX2NhbmFkYV90aW1ldHJlZS5ud2siKQ0KDQojIEltcG9ydCBvdXIgbWV0YWRhdGENClNDMl9tZXRhZGF0YS5kZiA8LSByZWFkX3RzdigiZGF0YS9uZXh0c3RyYWluX25jb3Zfbm9ydGgtYW1lcmljYV9jYW5hZGFfbWV0YWRhdGEudHN2IikNCmBgYA0KDQpgYGB7cn0NCiMgV2hhdCBkb2VzIHRoZSB0cmVlIGxvb2sgbGlrZT8NCnN0cihTQzJfdmFyaWFudHNfdGltZS5waHlsbykNCg0KU0MyX3ZhcmlhbnRzX3RpbWUucGh5bG8NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuMi4xIFRoZSBgcGh5bG9gIG9iamVjdCBpcyBhIHNpbXBsZSBsaXN0DQoNCkZyb20gdGhlIGxvb2tzIG9mIG91ciBzdHJ1Y3R1cmUsIGFmdGVyIHJlYWRpbmcgaW4gb3VyIE5ld2ljayBmaWxlLCB3ZSBoYXZlIGEgbGlzdCB3aXRoIHNpeCBlbGVtZW50cy4gVGhleSBhcmUgcHJldHR5IGNsZWFybHkgbmFtZWQgd2l0aCBlZGdlIGluZm9ybWF0aW9uIChhIG1hdHJpeCBvZiBwYWlyZWQgbnVtYmVycyksIGVkZ2UgbGVuZ3RocywgYSBjb3VudCBvZiB0aGUgbnVtYmVyIG9mIG5vZGVzIChpZSAqaW50ZXJuYWwqIHBvaW50cyB3aGVyZSBicmFuY2hlcyBiaWZ1cmNhdGUuIFdoaWxlIG1vc3RseSBqdXN0IG51bWJlcnMsIHNvbWUgb2YgdGhlc2UgYG5vZGUubGFiZWxgIHZhbHVlcyBhcHBlYXIgdG8gYmUgYmFzZWQgb24gdHJhdmVsIGhpc3RvcnkgaW5mb3JtYXRpb24uIEFsbCBvZiB0aGUgNiw2MjcgYHRpcC5sYWJlbGAgdmFsdWVzIGNvcnJlc3BvbmQgdG8gNiw2MjcgU0FSUy1Db1YtMiBzdHJhaW4gbmFtZXMgZm91bmQgaW4gdGhlIG1ldGFkYXRhIGZpbGUgYXMgd2VsbC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjIuMiBDb252ZXJ0IHRoZSB0cmVlIG9iamVjdCB0byBhIHRpYmJsZSBmb3IgYWRkaW5nIGV4dGVybmFsIGluZm9ybWF0aW9uDQoNClN1cHBvc2Ugd2Ugd2FudCB0byBjb21iaW5lIHNvbWUgaW5mb3JtYXRpb24gZnJvbSBvdXIgbWV0YWRhdGEgd2l0aCBvdXIgdHJlZT8gV2UgY2FuIHRoZW4gdXNlIHRoaXMgaW5mb3JtYXRpb24gdG8gY29sb3VyLCBzaGFwZSwgYW5kIGJyaW5nIG1vcmUgdmlzdWFsIG9yZGVyIHRvIG91ciB0cmVlLiBXb3JraW5nIHdpdGggdGhlIHRyZWUsIGhvd2V2ZXIsIGNhbiBiZSBhIGxpdHRsZSBoYXJkIGdpdmVuIHRoYXQgd2UgaGF2ZSBsaXN0cyBvZiBkaWZmZXJlbnQgbGVuZ3RocyByZXByZXNlbnRpbmcgZGlmZmVyZW50IHBvcnRpb25zIG9mIHRoZSB0cmVlLg0KDQpJbiBvdXIgY2FzZSwgd2Ugb25seSBoYXZlIHRyZWUgdGlwIGluZm9ybWF0aW9uIHRoYXQgd2Ugd2FudCB0byBhZGQgdG8gYW5kIHRoZSBlYXNpZXN0IHdheSB0byB3b3JrIHdvdWxkIGJlIGlmIGl0IHdhcyBpbiBhIHRhYmxlIGZvcm1hdC4gVGhlIHBhY2thZ2UgYHRpZHl0cmVlYCBwcm92aWRlcyBhIGZ1bmN0aW9uIGBhc190aWJibGVgIHRoYXQgd2lsbCBwYXJzZSBhbmQgY29udmVydCB0aGUgYHBoeWxvYCBmb3JtYXQgdG8gYSBgdGJsX3RyZWVgIG9iamVjdCwgd2hpY2ggaXMgYSBraW5kIG9mIHRpZHkgZGF0YSBmcmFtZS4NCg0KTGV0J3MgY29udmVydCBvdXIgdmFyaWFudCBwaHlsbyBvYmplY3QgdG8gc29tZXRoaW5nIHdlIGNhbiB3b3JrIHdpdGguDQoNCmBgYHtyfQ0KIyBDb252ZXJ0IHRoZSBwaHlsbyBvYmplY3QgdXNpbmcgYXNfdGliYmxlDQpTQzJfdmFyaWFudHNfdGltZS50YiA8LSBhc190aWJibGUoU0MyX3ZhcmlhbnRzX3RpbWUucGh5bG8pDQoNCiMgQ2hlY2sgb3V0IHRoZSBzdHJ1Y3R1cmUgbm93DQpzdHIoU0MyX3ZhcmlhbnRzX3RpbWUudGIpDQoNCmhlYWQoU0MyX3ZhcmlhbnRzX3RpbWUudGIpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuMy4wIFVwZGF0aW5nIG91ciB0cmVlIHdpdGggZXh0ZXJuYWwgaW5mb3JtYXRpb24NCg0KQXMgeW91IGNhbiBzZWUgb3VyIGBTQzJfdmFyaWFudHNfdGltZS50YmAgb2JqZWN0IGlzIGEgc2ltcGxlIGRhdGEgZnJhbWUgbm93IHJlcHJlc2VudGVkIGFzIGEgNC1jb2x1bW4gdGFibGUgd2l0aCAxMiw3Nzcgcm93cy4gVGhlIGVkZ2UgZGF0YSBpcyBlbmNvZGVkIGJldHdlZW4gdGhlIGBwYXJlbnRgIGFuZCBgbm9kZWAgY29sdW1ucyB3aXRoIGBicmFuY2gubGVuZ3RoYCB0byBkZWZpbmUgdGhlIGxlbmd0aCBvZiBlYWNoIGVkZ2UuIFdlIGFsc28gaGF2ZSBhbGwgb2YgdGhlIHRpcCBhbmQgbm9kZSBuYW1lcyBzdG9yZWQgdW5kZXIgdGhlIGBsYWJlbGAgY29sdW1uLiBXaXRoIHRoZSB0cmVlIGluIHRoaXMgZm9ybWF0LCB3ZSBjYW4gbm93IHRyZWF0IHRoZSB0cmVlIGxpa2UgYSB0aWJibGUgYW5kIGpvaW4gYWRkaXRpb25hbCBpbmZvcm1hdGlvbiB0byB0aGUgdHJlZS4NCg0KUnVsZXMgdG8ga2VlcCBpbiBtaW5kOg0KDQoxLiAgRG9uJ3QgbG9zZSBhbnkgbm9kZSBpbmZvcm1hdGlvbiBmcm9tIHlvdXIgb3JpZ2luYWwgdHJlZSBzdHJ1Y3R1cmUuIExvc2luZyAib2JzZXJ2YXRpb25zIiBpcyBlc3NlbnRpYWxseSBsb3NpbmcgZWRnZXMgb2YgeW91ciB0cmVlIQ0KDQoyLiAgVHJ5IHRvIHdvcmsgd2l0aCBhIGNvbXBsZXRlIGRhdGFzZXQgb2YgaW5mb3JtYXRpb24gYWx0aG91Z2ggc29tZXRpbWVzIGl0IGRvZXNuJ3QgbWFrZSBzZW5zZSB0aGF0IHlvdSB3b3VsZCBoYXZlIGl0IGFsbC4NCg0KMy4gIENvbnZlcnQgeW91ciB0aWJibGUgYmFjayB0byBhIHRyZWUgZm9ybWF0IHdoZW4geW91J3JlIGRvbmUgd2l0aCBgYXMudHJlZWRhdGEoKWANCg0KWW91IG1heSBoYXZlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBsZWF2ZXMgb3IgdGlwcyBvZiB5b3VyIHRyZWUgYnV0IGJ5IGRlZmF1bHQgdGhlcmUgaXMgbm8gcmVhbCBpbnRlcm5hbCBub2RlIGluZm9ybWF0aW9uIHdoZW4gaXQgY29tZXMgdG8gc2FtcGxlIG9yaWdpbnMgb3IgbGluZWFnZS4gV2hlbiBqb2luaW5nIGluZm9ybWF0aW9uIHRvIHRoZSB0YWJsZXMsIHVzZSB0aGUgY29ycmVjdCBkaXJlY3Rpb24gYCpfam9pbigpYCB0byBhdm9pZCBkYXRhIGxvc3MuIEEgYGZ1bGxfam9pbigpYCBpcyBwcm9iYWJseSB0aGUgc2FmZXN0Lg0KDQpMZXQncyB0YWtlIGEgcXVpY2sgbG9vayBhdCBvdXIgbWV0YWRhdGEgYW5kIGlkZW50aWZ5IHdoYXQgd2UncmUgaW50ZXJlc3RlZCBpbi4NCg0KYGBge3J9DQojIExvb2sgYXQgdGhlIG1ldGFkYXRhLCB3aGljaCBiaXRzIG9mIGluZm9ybWF0aW9uIHdvdWxkIGJlIG5pY2UgdG8gYWRkPw0Kc3RyKFNDMl9tZXRhZGF0YS5kZiwgZ2l2ZS5hdHRyID0gRkFMU0UpDQpgYGANCg0KYGBge3J9DQojIENoZWNrIG91dCBzb21lIG9mIHRoZSB2YWx1ZXMgZm9yIHRoZXNlIHZhcmlhYmxlcw0KDQojIE1ldGFkYXRhIGluZm9ybWF0aW9uIG9uIHRoZSBzYW1wbGVzDQp1bmlxdWUoU0MyX21ldGFkYXRhLmRmJENvdW50cnkpDQoNCnVuaXF1ZShTQzJfbWV0YWRhdGEuZGYkQWdlKQ0KDQojIE1ldGFkYXRhIGluZm9ybWF0aW9uIG9uIHRoZSBnZW5vbWVzDQp1bmlxdWUoU0MyX21ldGFkYXRhLmRmJCdHSVNBSUQgY2xhZGUnKQ0KDQp1bmlxdWUoU0MyX21ldGFkYXRhLmRmJCdOZXh0c3RyYWluIGNsYWRlJykNCg0KdW5pcXVlKFNDMl9tZXRhZGF0YS5kZiRDbGFkZSkNCg0KdW5pcXVlKFNDMl9tZXRhZGF0YS5kZiQnUEFOR08gbGluZWFnZScpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjMuMSBQaWNrIHRoZSB0aXAgYXR0cmlidXRlcyB0aGF0IGFyZSBtb3N0IGludGVyZXN0aW5nIG9yIHJlbGV2YW50DQoNCkZvciBvdXIgcHVycG9zZXMgaXQgbG9va3MgbGlrZSB3ZSB3aWxsIHdhbnQgdG8gZXhwbG9yZSBvdXIgZGF0YSB3aXRoOg0KDQotICAgR0lTQUlEIGNsYWRlOiBBICoqZyoqbG9iYWwgKippKipuaXRpYXRpdmUgb24gKipzKipoYXJpbmcgKiphKip2aWFuICoqaSoqbmZsdWVuemEgKipkKiphdGEgc2NpZW50aWZpYyBjb25zb3J0aXVtIG9mIGNsYWRlIG5hbWluZy4NCg0KLSAgIE5leHRzdHJhaW4gY2xhZGU6IENvbXB1dGF0aW9uYWxseSBsYWJlbGVkIGNsYWRlIGluZm9ybWF0aW9uIGJhc2VkIG9uIHRoZSBbTmV4dHN0cmFpbl0oaHR0cHM6Ly9uZXh0c3RyYWluLm9yZy8pIGNyaXRlcmlhIGFuZCBhbmFseXNpcyBvZiBuZXcgYW5kIGNvbnRpbnVpbmcgc3RyYWlucyBmcm9tIENPVklEIGdlbm9taWMgZGF0YS4NCg0KLSAgIEVtZXJnaW5nIE5leHRzdHJhaW4gY2xhZGUuDQoNCi0gICBQQU5HTyBsaW5lYWdlOiBhIGR5bmFtaWMgbm9tZW5jbGF0dXJlIG9mIFtTQVJTLUNvVi0yIGxpbmVhZ2VzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9QaHlsb2dlbmV0aWNfQXNzaWdubWVudF9vZl9OYW1lZF9HbG9iYWxfT3V0YnJlYWtfTGluZWFnZXMpLg0KDQotICAgQ291bnRyeTogVGhlIGNvdW50cnkgd2hlcmUgdGhlIHNhbXBsZSB3YXMgY29sbGVjdGVkLg0KDQotICAgQWRtaW4gRGl2aXNpb246IGdlb2dyYXBoaWMgbG9jYXRpb24gb2YgdGhlIHBhcnRpY3VsYXIgc3RyYWluL2Nhc2UuDQoNCi0gICBBZ2U6IHRoZSBhZ2Ugb2YgdGhlIHBhdGllbnRzIGZyb20gd2hpY2ggdGhlIHNhbXBsZSB3YXMgY29sbGVjdGVkIChpZiBpbmNsdWRlZCkNCg0KLSAgIENvbGxlY3Rpb24gRGF0YTogdGhlIGRhdGUgdGhlIGRhdGEgd2FzIHB1Ymxpc2hlZCBvciBjb2xsZWN0ZWQuDQoNCi0gICBTdHJhaW46IHRoZSBuYW1lIG9mIHRoZSBzdHJhaW4gd2hpY2ggd2UnbGwgbmVlZCBmb3IgbWVyZ2luZyB3aXRoIG91ciB0cmVlIHN0cnVjdHVyZS4NCg0KTGV0J3MgcHVsbCB0aGF0IGluZm9ybWF0aW9uIGRvd24gYW5kIGFkZCBpdCB0byBvdXIgYHRibF90cmVlYCBiZWZvcmUgY29udmVydGluZyBpdCB0byBhIGB0cmVlZGF0YWAgb2JqZWN0Lg0KDQpgYGB7cn0NCiMgQWRkIHBoeWxvZ2VuZXRpYyBpbmZvcm1hdGlvbiB0byBvdXIgdHJlZQ0KU0MyX3ZhcmlhbnRzX3RpbWUudHJlZSA8LQ0KDQogICMgUGFzcyBhbG9uZyB0aGUgbWV0YWRhdGENCiAgU0MyX21ldGFkYXRhLmRmICU+JSANCiAgDQogICMgU2VsZWN0IGp1c3QgYSBoYW5kZnVsIG9mIGF0dHJpYnV0ZXMgdG8gZ28gdG8gdGhlIHRyZWUNCiAgc2VsZWN0KFN0cmFpbiwgJ0dJU0FJRCBjbGFkZScsICdFbWVyZ2luZyBOZXh0c3RyYWluIGNsYWRlJywgDQogICAgICAgICAnUEFOR08gbGluZWFnZScsIENvdW50cnksICdBZG1pbiBEaXZpc2lvbicsICdDb2xsZWN0aW9uIERhdGEnLCBBZ2UpICU+JSANCiAgDQogIHJlbmFtZShzdHJhaW4gPSBTdHJhaW4sDQogICAgICAgICBHSVNBSUQgPSAnR0lTQUlEIGNsYWRlJywgDQogICAgICAgICBlbWVyZ2luZ19uZXh0c3RyYWluID0gJ0VtZXJnaW5nIE5leHRzdHJhaW4gY2xhZGUnLA0KICAgICAgICAgUEFOR08gPSAnUEFOR08gbGluZWFnZScsDQogICAgICAgICBzdHJhaW5fY291bnRyeSA9IENvdW50cnksDQogICAgICAgICBzdHJhaW5fZGl2aXNpb24gPSAnQWRtaW4gRGl2aXNpb24nLCANCiAgICAgICAgIHN0cmFpbl9kYXRlID0gJ0NvbGxlY3Rpb24gRGF0YScpICU+JSANCiAgDQogICMgZnVsbF9qb2luIHRvIG91ciB0cmVlIHRvIGVuc3VyZSBubyBkYXRhIGlzIGxvc3QgYWx0aG91Z2ggeW91IGNvdWxkIGFsc28gY2FyZWZ1bGx5IHVzZSBhIGxlZnRfam9pbg0KICBmdWxsX2pvaW4oeD1TQzJfdmFyaWFudHNfdGltZS50YiwgeT0uLCBieT1jKCJsYWJlbCIgPSAic3RyYWluIikpICU+JSANCiAgDQogICMgQ29udmVydCB0byBhIHRpZHl0cmVlIGZvcm1hdA0KICBhcy50cmVlZGF0YSgpDQoNCiMgTG9vayBhdCB0aGUgcmVzdWx0aW5nIHN0cnVjdHVyZQ0Kc3RyKFNDMl92YXJpYW50c190aW1lLnRyZWUpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjMuMS4xIEhvdyB0byBhY2Nlc3MgUzQgb2JqZWN0IHR5cGVzDQoNClRha2UgYSBxdWljayBsb29rIGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhpcyB0cmVlZGF0YSBvYmplY3QuIFdoYXQgZG8gd2Ugc2VlPyBUaGUgYHRyZWVkYXRhYCBjbGFzcyBoYXMgY29udmVydGVkIG91ciB0aWJibGUgYmFjayBpbnRvIGFuICoqUzQqKiBvYmplY3Qgd2l0aCAxMSBzbG90cy4gRWFjaCBzbG90LCBpcyBlc3NlbnRpYWxseSBhIHBsYWNlaG9sZGVyIGZvciBhbm90aGVyIG9iamVjdC4gV2UgY2FuIGFjY2VzcyB0aGVzZSBzbG90cyB1c2luZyB0aGUgYEBgIG9wZXJhdG9yIGFuZCB3ZSBjYW4gZnVydGhlciBhY2Nlc3Mgc3ViIGVsZW1lbnRzIHdpdGggdGhlIGAkYCBvcGVyYXRvci4NCg0KRm9yIGluc3RhbmNlLCB3ZSBjYW4gc2VlIHRoZXJlIGlzIGEgYHBoeWxvYCBzbG90IHdoaWNoIGxvb2tzIHN1c3BpY2lvdXNseSBsaWtlIG91ciBvcmlnaW5hbCBgU0MyX3ZhcmlhbnRzX3RpbWUucGh5bG9gIG9iamVjdC4gVGhlIHJlc3Qgb2Ygb3VyIGRhdGEgZnJhbWUgaW5mb3JtYXRpb24sIHdoaWNoIHdlIGp1c3QgYWRkZWQgdG8gdGhlIGB0YmxfdHJlZWAgYmVmb3JlIGNvbnZlcnRpbmcgaXQgaXMgc3RvcmVkIGluIHRoZSBgZGF0YWAgc2xvdC4gVGhlcmUgYXJlIGFkZGl0aW9uYWwgc2xvdHMgd2hlcmUgd2UgY2FuIGFkZCBzZXF1ZW5jaW5nIGluZm9ybWF0aW9uIGZvciBjb21wYXJpc29uIGluIGRpZmZlcmVudCB0eXBlcyBvZiBwaHlsb2dlbmV0aWMgdHJlZSB2aXN1YWxpemF0aW9ucy4NCg0KYGBge3J9DQojIEdyYWIgb3VyIHBoeWxvIG9iamVjdA0KU0MyX3ZhcmlhbnRzX3RpbWUudHJlZUBwaHlsbw0KDQojIFRha2UgYSBwZWVrIGF0IG91ciBhc3NvY2lhdGVkIG5vZGUgZGF0YQ0KaGVhZChTQzJfdmFyaWFudHNfdGltZS50cmVlQGRhdGEpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuNC4wIFBsb3QgeW91ciB0cmVlIHdpdGggYGdndHJlZSgpYCBmcm9tIHRoZSBgZ2d0cmVlYCBwYWNrYWdlDQoNCk5vdyB0aGF0IHdlJ3ZlIHB1dCBzb21lIGV4dHJhIGRhdGEgaW50byBvdXIgdHJlZSwgd2UgYXJlIHJlYWR5IHRvIHBsb3QgaXQgd2l0aCB0aGUgaGVscCBvZiB0aGUgYGdndHJlZSgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZ2d0cmVlYCBwYWNrYWdlLiBJZiB5b3UgaGF2ZW4ndCBub3RpY2VkIHlldCwgYHRyZWVpb2AsIGB0aWR5dHJlZWAgYW5kIGBnZ3RyZWVgIGZvcm0gYSBzdWl0ZSBvZiBwYWNrYWdlcyB0aGF0IHdlIGNhbiB1c2UgdG8gaW1wb3J0LCBhbHRlciwgYW5kIHZpc3VhbGl6ZSBvdXIgdHJlZXMuDQoNClBhcmFtZXRlcnMgZm9yIGBnZ3RyZWUoKWAgaW5jbHVkZToNCg0KLSAgIGB0cmA6IHRoZSBwaHlsbyB0cmVlIG9iamVjdA0KDQotICAgYGxheW91dGA6IHRoZSBzaGFwZSBvZiB0aGUgdHJlZSBpbmNsdWRpbmc6IHJlY3Rhbmd1bGFyLCBkZW5kcm9ncmFtLCBzbGFudGVkLCBlbGxpcHNlLCBmYW4sIGNpcmN1bGFyLCBpbndhcmRfY2lyY3VsYXIsIGFuZCByYWRpYWwuDQoNCi0gICBgbXJzZGA6IG1vc3QgcmVjZW50IHNhbXBsaW5nIGRhdGUgdXNlZCBmb3Igc2V0dGluZyBhIHRpbWUtYmFzZWQgc2NhbGUNCg0KLSAgIGBhcy5EYXRlYDogbG9naWNhbCB0byBzcGVjaWZ5IGlmIGRhdGUgd2lsbCBiZSBpbiBjYWxlbmRhciB2cyBkZWNpbWFsLWRhdGUgZm9ybWF0DQoNCi0gICBgYnJhbmNoLmxlbmd0aGA6IHZhcmlhYmxlIGZvciBzY2FsaW5nIGJyYW5jaCBsZW5ndGgNCg0KLSAgIGByb290LnBvc2l0aW9uYDogdGhlIHBvc2l0aW9uIG9mIG91ciByb290IG5vZGUgKGRlZmF1bHQgPSAwKQ0KDQpZb3UnbGwgbm90aWNlIHRoYXQgYGdndHJlZSgpYCBhcHBlYXJzIHRvIGFjdCB2ZXJ5IG11Y2ggbGlrZSB0aGUgYGdncGxvdCgpYCBjb21tYW5kLiBXZSdsbCBiZSBhYmxlIHRvIGFkZCBsYXllcnMgdG8gdGhlIGJhc2UgdmlzdWFsaXphdGlvbiBtdWNoIGxpa2Ugd2UgZG8gd2l0aCBvdGhlciBnZ3Bsb3Qgb2JqZWN0cy4gV2UnbGwgYWxzbyB1c2UgYHRoZW1lX3RyZWUyKClgIHdoaWNoIHdpbGwgYXV0b21hdGljYWxseSBhZGQgYW4geC1heGlzIHNjYWxlIHRvIG91ciB0cmVlLg0KDQpXZSdsbCBzdGFydCBzaW1wbGUgYnkganVzdCB2aXN1YWxpemluZyBhbGwgNiw2MjcgdGlwcyBvZiBvdXIgdHJlZS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0NCg0KIyBTaG93IHRoZSBlbnRpcmUgdHJlZQ0KDQpTQzJfdmFyaWFudHNfdGltZS50cmVlICU+JSANCg0KIyAxLiBEYXRhDQpnZ3RyZWUodHIgPSAuKSArIA0KICAjIDIuIEFlc3RoZXRpY3MNCiAgYWVzKGNvbG9yID0gZW1lcmdpbmdfbmV4dHN0cmFpbikgKyANCiAgDQogICMgVGhlbWVzDQogIHRoZW1lX3RyZWUyKCkgKyANCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKyAjIE1vdmUgb3VyIGxlZ2VuZCB0byB0aGUgYm90dG9tDQoNCiAgIyBQcm92aWRlIHNvbWUgbGFiZWxzDQogIGxhYnMoeD0iVGltZSIsDQogICAgICAgdGl0bGUgPSAiQ2FuYWRhIGFuZCBVUyBzZXF1ZW5jZWQgc3RyYWluIHBoeWxvZ2VueSIpICsNCg0KICAjIE1ha2Ugb3VyIGd1aWRlIGxpbmVzIGEgbGl0dGxlIHRoaWNrZXIgDQogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiTmV4dHN0cmFpblxuY2xhZGUiLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTIpKSkgICANCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMS41LjAgRmlsdGVyIHlvdXIgdHJlZSB1c2luZyBgZHJvcC50aXAoKWANCg0KTG9va2luZyBhdCBvdXIgdmlzdWFsaXphdGlvbiwgdGhlcmUgYXJlIGEgbG90IG9mIHRpcHMgdG8gZGlzcGxheSBhbmQgd2UndmUgY29sb3VyZWQgdGhlIHRyZWUgYWxvbmcgdGhlIGJyYW5jaGVzLiBBbm90aGVyIHRoaW5nIHdlIGNhbiBzZWUgaXMgdGhhdCB0aGVyZSBpcyBhbiAiTkEiIHZhbHVlLiBUaGlzIG1vc3RseSByZXByZXNlbnRzIHRoZSBicmFuY2hlcyBvZiB0aGUgdHJlZSBjb25uZWN0aW5nIG5vZGVzIGludGVybmFsbHkgc2luY2UgdGhleSBkbyBub3QgaGF2ZSBhIHNwZWNpZmljIGxpbmVhZ2UgYXNzb2NpYXRlZCBmcm9tIG91ciBkYXRhLg0KDQpBcyBpdCBzdGFuZHMsIHRoZXJlJ3MganVzdCB3YXkgdG9vIG11Y2ggZGF0YSBoZXJlIHRvIGxvb2sgYXQgYW5kIHdvcmsgd2l0aC4gV2UgY2FuIHRyaW0gdGhhdCBkYXRhIHVzaW5nIHRoZSBgZHJvcC50aXAoKWAgZnVuY3Rpb24gd2l0aG91dCB3b3JyeWluZyBhYm91dCBob3cgdGhlIHRyZWUgaXMgcGFyc2VkLiBJbnRlcm5hbGx5LCBhbGwgdGhhdCBwcnVuaW5nIHdpbGwgdGFrZSBwbGFjZSB3aXRoaW4gdGhlIGZ1bmN0aW9uLiBUbyBkbyB0aGlzLCB3ZSdsbCBnZW5lcmF0ZSBhIGxpc3Qgb2YgdGlwcyB3ZSB3YW50IHRvICpyZW1vdmUqIGZpcnN0Lg0KDQpgYGB7cn0NCiMgWW91IGNhbiBtYWtlIGEgbGlzdCBvZiB0aXBzIHRvIGRyb3Agd2hlbiB5b3UncmUgbWFraW5nIHlvdXIgZ2d0cmVlDQoNCiMgR2VuZXJhdGUgYSB0YWJsZSBvZiB0cmVlIGluZm9ybWF0aW9uIHRvIGRyb3ANCnRvX2Ryb3AgPC0NCg0KICBTQzJfbWV0YWRhdGEuZGYgJT4lIA0KICANCiAgIyBGaWx0ZXIgZm9yIHN0cmFpbnMgdGhhdCBhcmUgbm90IGZyb20gQ2FuYWRhIGFuZCBhcmUgZnJvbSBiZWZvcmUgMjAyMC0xMS0wMQ0KICBmaWx0ZXIoIShDb3VudHJ5ID09ICJDYW5hZGEiICYNCiAgICAgICAgICAgYENvbGxlY3Rpb24gRGF0YWAgPj0gYXMuRGF0ZSgiMjAyMC0xMS0wMSIpDQogICAgICAgICAgKSkgJT4lDQogIA0KICAjIEp1c3Qga2VlcCB0aGUgc3RyYWluIGluZm9ybWF0aW9uDQogIHB1bGwoU3RyYWluKQ0KDQojIFRha2UgYSBsb29rIGF0IHdoYXQgd2UgZ290IGJhY2sNCnN0cih0b19kcm9wKQ0KYGBgDQoNCmBgYHtyfQ0KIyBBdCB0aGUgc2FtZSB0aW1lIG1ha2UgYSBkYXRhc2V0IHRvIGtlZXAuIFlvdSdsbCB1c2UgdGhpcyB0byB1cGRhdGUgdGhlIGluZm9ybWF0aW9uIGluIHRoZSB0cmVlIGxhdGVyDQp0b19rZWVwIDwtIFNDMl9tZXRhZGF0YS5kZiAlPiUgZmlsdGVyKCFTdHJhaW4gJWluJSB0b19kcm9wKQ0KDQojIFdoaWNoIHRpcHMgYXJlIHdlIGtlZXBpbmc/DQpzdHIodG9fa2VlcCwgZ2l2ZS5hdHRyID0gRkFMU0UpICAgICAgICAgDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuNi4wIFVwZGF0ZSBvdXIgcHJ1bmVkIHRyZWUgYW5kIGFkZCBzb21lIGV4dHJhIGBnZW9tXypgIGxheWVycw0KDQpUaGUgYGdndHJlZWAgcGFja2FnZSBicmluZ3MgYSBudW1iZXIgb2YgYGdncGxvdDJgLWNvbXBhdGlibGUgZ2VvbXMgdG8gb3VyIGZpbmdlci0qdGlwcyouIFdlJ2xsIHNwcnVjZSB1cCBvdXIgdHJlZSB3aXRoIHNvbWUgdGlwIGxhYmVscyB0byBiZWdpbiB3aXRoLiBXZSdsbCBhY2NvbXBsaXNoIHRoaXMgd2l0aCB0aGUgYGdlb21fdGlwbGFiKClgIGxheWVyLg0KDQp8IENvbXBvbmVudCB0eXBlIHwgTGF5ZXIgdHlwZSB8IENvbW1hbmQgICAgICAgICB8IERlc2NyaXB0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgICAgICBUcmVlICAgICAgfCBHZW9tICAgICAgIHwgZ2VvbV90cmVlICAgICAgIHwgdHJlZSBzdHJ1Y3R1cmUgbGF5ZXIsIHdpdGggbXVsdGlwbGUgbGF5b3V0IHN1cHBvcnRlZCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgIFRyZWUgICAgICB8IEdlb20gICAgICAgfCBnZW9tX3RyZWVzY2FsZSAgfCB0cmVlIGJyYW5jaCBzY2FsZSBsZWdlbmQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgTm9kZSAgICAgIHwgR2VvbSAgICAgICB8IGdlb21fbm9kZXBvaW50ICB8IGFubm90YXRlIGludGVybmFsIG5vZGVzIHdpdGggc3ltYm9saWMgcG9pbnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICBOb2RlICAgICAgfCBBbm5vdGF0aW9uIHwgZ2VvbV9yYW5nZSAgICAgIHwgYmFyIGxheWVyIHRvIHByZXNlbnQgdW5jZXJ0YWludHkgb2YgZXZvbHV0aW9uYXJ5IGluZmVyZW5jZSAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgIE5vZGUgICAgICB8IEFubm90YXRpb24gfCBnZW9tX3Jvb3Rwb2ludCAgfCBhbm5vdGF0ZSByb290IG5vZGUgd2l0aCBzeW1ib2xpYyBwb2ludCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICBCcmFuY2ggICAgIHwgQW5ub3RhdGlvbiB8IGdlb21fbGFiZWwyICAgICB8IG1vZGlmaWVkIHZlcnNpb24gb2YgZ2VvbV9sYWJlbCwgd2l0aCBzdWJzZXR0aW5nIHN1cHBvcnRlZCBmb3IgbGFiZWxsaW5nIGJyYW5jaGVzIHwNCnwgICAgIEJyYW5jaCAgICAgfCBBbm5vdGF0aW9uIHwgZ2VvbV9zZWdtZW50MiAgIHwgbW9kaWZpZWQgdmVyc2lvbiBvZiBnZW9tX3NlZ21lbnQsIHdpdGggc3Vic2V0dGluZyBzdXBwb3J0ZWQgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgIFRheGEgICAgICB8IEdlb20gICAgICAgfCBnZW9tX3BvaW50MiAgICAgfCBtb2RpZmllZCB2ZXJzaW9uIG9mIGdlb21fcG9pbnQsIHdpdGggc3Vic2V0dGluZyBzdXBwb3J0ZWQgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgVGF4YSAgICAgIHwgR2VvbSAgICAgICB8IGdlb21fdGlwcG9pbnQgICB8IGFubm90YXRlIGV4dGVybmFsIG5vZGVzIHdpdGggc3ltYm9saWMgcG9pbnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICBUYXhhICAgICAgfCBBbm5vdGF0aW9uIHwgZ2VvbV90YXhhbGluayAgIHwgYXNzb2NpYXRlIHR3byByZWxhdGVkIHRheGEgYnkgbGlua2luZyB0aGVtIHdpdGggYSBjdXJ2ZSAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgIFRheGEgICAgICB8IEFubm90YXRpb24gfCBnZW9tX3RleHQyICAgICAgfCBtb2RpZmllZCB2ZXJzaW9uIG9mIGdlb21fdGV4dCwgd2l0aCBzdWJzZXR0aW5nIHN1cHBvcnRlZCAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgVGF4YSAgICAgIHwgQW5ub3RhdGlvbiB8IGdlb21fdGlwbGFiICAgICB8IGxheWVyIG9mIHRpcCBsYWJlbHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICBUYXhhICAgICAgfCBBbm5vdGF0aW9uIHwgZ2VvbV90aXBsYWIyICAgIHwgbGF5ZXIgb2YgdGlwIGxhYmVscyBmb3IgY2lyY3VsYXIgbGF5b3V0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgQ2xhZGUgICAgICB8IEFubm90YXRpb24gfCBnZW9tX2JhbGFuY2UgICAgfCBoaWdobGlnaHRzIHRoZSB0d28gZGlyZWN0IGRlc2NlbmRhbnQgY2xhZGVzIG9mIGFuIGludGVybmFsIG5vZGUgICAgICAgICAgICAgICAgICB8DQp8ICAgICBDbGFkZSAgICAgIHwgQW5ub3RhdGlvbiB8IGdlb21fY2xhZGVsYWJlbCB8IGFubm90YXRlIGEgY2xhZGUgd2l0aCBiYXIgYW5kIHRleHQgbGFiZWwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgIENsYWRlICAgICAgfCBBbm5vdGF0aW9uIHwgZ2VvbV9oaWxpZ2h0ICAgIHwgaGlnaGxpZ2h0IGEgY2xhZGUgd2l0aCByZWN0YW5nbGUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgQ2xhZGUgICAgICB8IEFubm90YXRpb24gfCBnZW9tX3N0cmlwICAgICAgfCBhbm5vdGF0ZSBhc3NvY2lhdGVkIHRheGEgd2l0aCBiYXIgYW5kIChvcHRpb25hbCkgdGV4dCBsYWJlbCAgICAgICAgICAgICAgICAgICAgICB8DQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9DQojIFNob3cgb3VyIHBydW5lZCB0cmVlDQoNClNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lDQogICMgZHJvcCB0aXBzIGJhc2VkIG9uIG91ciBsaXN0DQogIGRyb3AudGlwKC4sIHRvX2Ryb3ApICU+JSANCg0KICAjIDEuIERhdGENCiAgZ2d0cmVlKHRyID0gLikgKyAjIE5lZWQgdGhlIG1vc3QgcmVjZW50IHNhbXBsaW5nIGRhdGUNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoY29sb3IgPSBlbWVyZ2luZ19uZXh0c3RyYWluKSArIA0KICAgIA0KICAgICMgVGhlbWVzDQogICAgdGhlbWVfdHJlZTIoKSArIA0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKyAjIE1vdmUgb3VyIGxlZ2VuZCB0byB0aGUgYm90dG9tDQogIA0KICAgICMgUHJvdmlkZSBzb21lIGxhYmVscw0KICAgIGxhYnMoeD0iVGltZSIsDQogICAgICAgICB0aXRsZSA9ICJTdWJzZXQgb2YgQ2FuYWRpYW4gc2VxdWVuY2VkIHN0cmFpbnMiKSArDQogICAgDQogICAgIyBNYWtlIG91ciBndWlkZSBsaW5lcyBhIGxpdHRsZSB0aGlja2VyIA0KICAgIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiTmV4dHN0cmFpblxuY2xhZGUiLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9MikpKSArICAgIA0KICAgIA0KICAgICMgNC4gR2VvbXMNCiAgICAjIyMgMS42LjAgQWRkIHRpcHMgbGFiZWxzIHRvIG91ciB0cmVlIHRpcHMNCiAgICBnZW9tX3RpcGxhYigpICAgIA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS42LjEgUmVwbGFjZSBsYWJlbHMgd2l0aCBwb2ludHMgdXNpbmcgZGlmZmVyZW50IGdlb21zDQoNCkluIGEgdHJlZSB0aGlzIHBhY2tlZCwgeW91IGNhbid0IHJlYWxseSByZWFkIHRoZSB0aXAgbGFiZWxzLiBUaGUgZGF0YSB0aGF0J3MgbW9zdCBoZWxwZnVsLCBob3dldmVyLCBpcyB0aGUgY29sb3VyaW5nLiBZb3UgY2FuIHJlcGxhY2UgeW91ciBsYWJlbHMgd2l0aCBwb2ludHMgaW4gMyB3YXlzOg0KDQotICAgYGdlb21fcG9pbnQoKWANCg0KLSAgIGBnZW9tX25vZGVwb2ludCgpYA0KDQotICAgYGdlb21fdGlwcG9pbnQoKWANCg0KSWYgd2UgdXNlIGBnZW9tX3BvaW50KClgIHdlIGNhbiBjb2xvdXIvYW5ub3RhdGUgYWxsIG9mIHRoZSBub2RlcyBhbmQgdGlwcyBidXQgd2UgYWxyZWFkeSBrbm93IHRoYXQgdGhlcmUgaXNuJ3QgYW55IHBoeWxvZ2VuZXRpYyBpbmZvcm1hdGlvbiBhc3NvY2lhdGVkIHdpdGggb3VyIGludGVybmFsIG5vZGVzLiBMaWtld2lzZSwgYGdlb21fbm9kZXBvaW50KClgIHdpbGwgYWxsb3cgdXMgdG8gY29sb3VyIHRoZSBub2RlcyBidXQgdGhlcmUgaXNuJ3QgYW55IGdyb3VwaW5nIGluZm9ybWF0aW9uIGFzc29jaWF0ZWQgd2l0aCB0aGF0Lg0KDQpUaGVyZWZvcmUgd2UnbGwgdXNlIGBnZW9tX3RpcHBvaW50KClgIHRvIGNvbG91ciBvdXIgdGlwcyBiYXNlZCBvbiB0aGVpciBOZXh0c3RyYWluIGNsYWRlIGluZm9ybWF0aW9uIGFuZCB3ZSBjYW4gYWx0ZXIgdGhlIHRpcCBzaGFwZSB0byBtYXRjaCB0aGUgcHJvdmluY2Ugd2hlcmUgdGhlIHN0cmFpbiB3YXMgc2VxdWVuY2VkLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQ0KIyBTaG93IGVudGlyZSB0cmVlDQoNClNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lDQogIGRyb3AudGlwKC4sIHRvX2Ryb3ApICU+JSANCg0KICAjIDEuIERhdGENCiAgZ2d0cmVlKHRyID0gLikgKyAjIE5lZWQgdGhlIG1vc3QgcmVjZW50IHNhbXBsaW5nIGRhdGUNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoY29sb3IgPSBlbWVyZ2luZ19uZXh0c3RyYWluKSArIA0KICAgIA0KICAgICMgVGhlbWVzDQogICAgdGhlbWVfdHJlZTIoKSArIA0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgIyBNb3ZlIG91ciBsZWdlbmQgdG8gdGhlIGJvdHRvbQ0KICAgICAgICAgIGxlZ2VuZC5ib3ggPSAidmVydGljYWwiKSArICAjIFN0YWNrIGxlZ2VuZHMgdmVydGljYWxseQ0KICANCiAgICAjIFByb3ZpZGUgc29tZSBsYWJlbHMNCiAgICBsYWJzKHg9IlRpbWUiLA0KICAgICAgICAgdGl0bGUgPSAiU3Vic2V0IG9mIENhbmFkaWFuIHNlcXVlbmNlZCBzdHJhaW5zIikgKw0KICAgIA0KICAgICMgTWFrZSBvdXIgZ3VpZGUgbGluZXMgYSBsaXR0bGUgdGhpY2tlciANCiAgICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIk5leHRzdHJhaW5cbmNsYWRlIiwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTIpKSwNCiAgICAgICAgICAgc2hhcGUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiU3RyYWluXG5sb2NhdGlvbiIpDQogICAgICAgICAgKSArIA0KICANCiAgICAjIDMuIFNjYWxpbmcNCiAgICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygxNToyMCwgMTEpKSArDQogICAgDQogICAgIyA0LiBHZW9tcw0KICAgICMgQWRkIHRpcHMgbGFiZWxzIHRvIG91ciB0cmVlIHRpcHMNCiAgICAjIyMgMS42LjEgQ2hhbmdlIG91ciB0aXAgc2hhcGUgYnkgc3RyYWluX2RpdmlzaW9uDQogICAgZ2VvbV90aXBwb2ludChhZXMoc2hhcGUgPSBzdHJhaW5fZGl2aXNpb24pLCBhbHBoYSA9IDAuNywgc2l6ZSA9IDMpICAjIEFkZCB0aXBzIG9ubHkgICAgDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjYuMiBGaXggeW91ciB4LWF4aXMgd2l0aCB0aGUgYG1yc2RgIHBhcmFtZXRlcg0KDQpBcyB5b3UgY2FuIHNlZSB3ZSBhcmUgd29ya2luZyB3aXRoIGEgdGltZS1iYXNlZCBheGlzIGJ1dCBpdCBhbGwgYXBwZWFycyB0byBiZSBvbiBhIHJlbGF0aXZlIHNjYWxlIGFuZCB3aGF0IHdlJ2QgcmVhbGx5IGxpa2UgdG8gc2VlIGlzIHJlYWwtd29ybGQgdGltZS4gSW4gb3JkZXIgdG8gZG8gdGhhdCwgd2UgY2FuIGFzc2lnbiB0aGUgYG1yc2RgIHBhcmFtZXRlciB0byB0aGUgbW9zdCByZWNlbnQgc2FtcGxpbmcgZGF0ZSBpbiBvdXIgZGF0YXNldC4gV2UnbGwgYWxzbyBzZXQgb3VyIHBhcmFtZXRlciBgYXMuRGF0ZWAgaW4gb3JkZXIgdG8gc2VlIHRoZSBkYXRlIGluIGEgY2FsZW5kYXIgZm9ybWF0IHJhdGhlciB0aGFuIGEgZGVjaW1hbC1kYXRlIGZvcm1hdC4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0NCiMgU2hvdyBlbnRpcmUgdHJlZQ0KDQpTQzJfdmFyaWFudHNfdGltZS50cmVlICU+JQ0KICBkcm9wLnRpcCguLCB0b19kcm9wKSAlPiUgDQogIA0KICAjIDEuIERhdGENCiAgZ2d0cmVlKHRyID0gLiwgDQogICAgICAgbXJzZCA9ICIyMDIxLTAyLTE3IiwgICAgIyMjIDEuNi4yIFNldCB0aGUgbW9zdCByZWNlbnQgc2FtcGxpbmcgZGF0ZQ0KICAgICAgIGFzLkRhdGUgPSBUUlVFKSArICAgICAgICMjIyAxLjYuMiBNYWtlIHN1cmUgaXQncyBzZXQgYXMgYSBkYXRhIA0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyhjb2xvciA9IGVtZXJnaW5nX25leHRzdHJhaW4pICsgDQogICAgDQogICAgIyBUaGVtZXMNCiAgICB0aGVtZV90cmVlMigpICsgDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAjIE1vdmUgb3VyIGxlZ2VuZCB0byB0aGUgYm90dG9tDQogICAgICAgICAgbGVnZW5kLmJveCA9ICJ2ZXJ0aWNhbCIpICsgICMgU3RhY2sgbGVnZW5kcyB2ZXJ0aWNhbGx5DQoNCiAgICAjIFByb3ZpZGUgc29tZSBsYWJlbHMNCiAgICBsYWJzKHg9IlRpbWUiLA0KICAgICAgICAgdGl0bGUgPSAiU3Vic2V0IG9mIENhbmFkaWFuIHNlcXVlbmNlZCBzdHJhaW5zIikgKw0KICAgIA0KICAgICMgTWFrZSBvdXIgZ3VpZGUgbGluZXMgYSBsaXR0bGUgdGhpY2tlciANCiAgICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIk5leHRzdHJhaW5cbmNsYWRlIiwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTIpKSwNCiAgICAgICAgICAgc2hhcGUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiU3RyYWluXG5sb2NhdGlvbiIpDQogICAgICAgICAgKSArIA0KDQogICAgIyAzLiBTY2FsaW5nDQogICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMTU6MjAsIDExKSkgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgICMgQWRkIHRpcHMgbGFiZWxzIHRvIG91ciB0cmVlIHRpcHMNCiAgICAjIENoYW5nZSBvdXIgdGlwIHNoYXBlIGJ5IHN0cmFpbl9kaXZpc2lvbg0KICAgIGdlb21fdGlwcG9pbnQoYWVzKHNoYXBlID0gc3RyYWluX2RpdmlzaW9uKSwgYWxwaGEgPSAwLjcsIHNpemUgPSAzKSAgIyBBZGQgdGlwcyBvbmx5ICAgIA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjcuMCBUaGluIG91dCB0aGUgdHJlZSBzb21lIG1vcmUgd2l0aCBgdmlld0NsYWRlKClgIGFuZCBvdGhlciBpbmZvcm1hdGlvbiBmdW5jdGlvbnMNCg0KTG9va2luZyBhdCB0aGUgdHJlZSB0aGVyZSBhcmUgc3RpbGwgcXVpdGUgYSBmZXcgbm9kZXMgYnV0IHdlIGNhbiB6b29tIGluIG9uIGEgc3BlY2lmaWMgY2xhZGUgdXNpbmcgdGhlIGB2aWV3Q2xhZGUoKWAgZnVuY3Rpb24uIFRvIGFjY29tcGxpc2ggdGhpcyB3ZSBuZWVkIHRvIGFjY2VzcyB0aGUgbW9zdCByZWNlbnQgY29tbW9uIGFuY2VzdG9yIChNUkNBKSBvZiBhIGdyb3VwLiBBdCB0aGUgc2FtZSB0aW1lLCB3ZSBzaG91bGQgdGFsayBhYm91dCB3YXlzIHRvIHRyYXZlcnNlIG9yIG5hdmlnYXRlIHRoaXMgdHJlZS4gSGVyZSdzIHdoZXJlIGB0aWR5dHJlZWAgb2ZmZXJzIHVwIGEgZmV3IGFkZGl0aW9uYWwgZnVuY3Rpb25zIGZvciBzZWFyY2hpbmcgeW91ciBgdGJsX3RyZWVgLiBUaGVzZSB0YWtlIG9uIHRoZSBmb3JtIG9mIGBmdW5jdGlvbih0YmxfdHJlZSwgbm9kZV9udW1iZXIpYCBvciBgZnVuY3Rpb24odGJsX3RyZWUsIG5vZGVfbGFiZWwpYA0KDQp8IGZ1bmN0aW9uICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IGNoaWxkKCkgICAgIHwgRmluZCB0aGUgY2hpbGRyZW4gb2YgYW4gaW50ZXJuYWwgbm9kZS4gICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBwYXJlbnQoKSAgICB8IEZpbmQgdGhlIHBhcmVudCBub2RlIG9mIGEgdGlwIG9yIG90aGVyIG5vZGUuICAgICAgICAgICAgICAgICAgIHwNCnwgb2Zmc3ByaW5nKCkgfCBGaW5kIGFsbCBvZiB0aGUgb2Zmc3ByaW5nIG9mIGEgbm9kZS4gICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGFuY2VzdG9yKCkgIHwgRmluZCBhbGwgb2YgdGhlIGFuY2VzdG9ycyBvZiBhIHRpcCBvciBub2RlICAgICAgICAgICAgICAgICAgICAgfA0KfCBzaWJsaW5nKCkgICB8IEZpbmQgdGhlIG5lYXJlc3Qgc2libGluZyBvZiBhIHRpcCAob3Igbm9kZSkgICAgICAgICAgICAgICAgICAgIHwNCnwgTVJDQSgpICAgICAgfCBGaW5kIHRoZSBtb3N0IHJlY2VudCBjb21tb24gYW5jZXN0b3IgYmV0d2VlbiB0d28gb3IgbW9yZSBub2RlcyB8DQoNCkxldCdzIGZpbHRlciBvdXIgdGF4YSBsaXN0IGEgbGl0dGxlIG1vcmUgc2VhcmNoaW5nIGZvciB2YXJpYW50cyBvZiB0aGUgY2xhZGUgNTAxWS5WMSBhbmQgNTAxWS5WMi4gVGhlbiB3ZSdsbCBmaW5kIHRoZSBNUkNBIGJldHdlZW4gdGhvc2UgdGlwcy4NCg0KYGBge3J9DQojIFlvdSBjYW4gbWFrZSBhIGxpc3Qgb2YgdGlwcyB0byBkcm9wIHdoZW4geW91J3JlIG1ha2luZyB5b3VyIGdndHJlZQ0KDQojIEdlbmVyYXRlIGEgdGFibGUgb2YgdHJlZSBpbmZvcm1hdGlvbiB0byBkcm9wDQp0b192aWV3IDwtDQoNCiAgU0MyX21ldGFkYXRhLmRmICU+JSANCiAgDQogICAgIyBGaWx0ZXIgb3VyIHRpcCBpbmZvcm1hdGlvbg0KICAgIGZpbHRlcihgRW1lcmdpbmcgTmV4dHN0cmFpbiBjbGFkZWAgJWluJSBjKCIyMEkvNTAxWS5WMSIsICIyMEgvNTAxWS5WMiIpICYgDQogICAgICAgICAgIENvdW50cnkgPT0gIkNhbmFkYSIgJg0KICAgICAgICAgICBgQ29sbGVjdGlvbiBEYXRhYCA+PSBhcy5EYXRlKCIyMDIxLTAyLTE2IikNCiAgICAgICAgICApICU+JSANCiAgDQogICAgIyBKdXN0IGtlZXAgdGhlIHN0cmFpbiBuYW1lcw0KICAgIHB1bGwoU3RyYWluKQ0KDQojIFdoYXQncyB0aGUgbGlzdCBsb29rIGxpa2U/DQp0b192aWV3DQpgYGANCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgTVJDQSBmcm9tIGEgc3Vic2V0IG9mIG91ciB0aXBzDQoNCnRvX3ZpZXdfTVJDQSA8LQ0KICAjIFBhc3MgYWxvbmcgb3VyIHRyZWUgaW5mb3JtYXRpb24NCiAgU0MyX3ZhcmlhbnRzX3RpbWUudHJlZSAlPiUNCiAgDQogICMgR2V0IHJpZCBvZiB0aGUgdGlwcyB3ZSB3b3VsZCBiZWZvcmUgKHRvIG1hdGNoIHRoZSB0cmVlIHdlJ2xsIHBsb3QpDQogIGRyb3AudGlwKC4sIHRvX2Ryb3ApICU+JQ0KICANCiAgIyBEZXRlcm1pbmUgdGhlIE1SQ0EgYmV0d2VlbiB0aGUgZmlyc3QgNCB0aXBzIGluIHRoZSBsaXN0DQogIE1SQ0EoLiwgdG9fdmlld1sxOjRdKQ0KDQojIFdoYXQga2luZCBvZiB2YWx1ZSBkb2VzIE1SQ0EoKSByZXR1cm4/DQp0b192aWV3X01SQ0ENCmBgYA0KDQpgYGB7cn0NCiMgSG93IG1hbnkgb2Zmc3ByaW5nIGRvZXMgaXQgaGF2ZT8NCg0KIyBQYXNzIGFsb25nIG91ciB0cmVlIGluZm9ybWF0aW9uDQpTQzJfdmFyaWFudHNfdGltZS50cmVlICU+JQ0KICANCiAgIyBEcm9wIG91ciBvcmlnaW5hbCBzZXQgb2YgdGlwcw0KICBkcm9wLnRpcCguLCB0b19kcm9wKSAlPiUgDQogICMgbWFrZSBhIHRpYmJsZSBzbyB3ZSBjYW4gc2VlIGFsbCB0aGUgcmVsZXZhbnQgaW5mb3JtYXRpb24NCiAgYXNfdGliYmxlKCkgJT4lIA0KICAjIEZpbmQgdGhlIG9mZnNwcmluZyBvZiBhIHNwZWNpZmljIG5vZGUNCiAgb2Zmc3ByaW5nKC4sIHRvX3ZpZXdfTVJDQSkgJT4lIA0KICAjIENvbnZlcnQgdG8gYSB0cmVlDQogIGFzLnRyZWVkYXRhKCkgJT4lIA0KICBzdHIoKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS43LjEgUGFzcyBhIGBnZ3Bsb3QyYCBvYmplY3QgdG8gYHZpZXdDbGFkZSgpYA0KDQpXZSBzZWUgdGhhdCBvdXIgY2hvc2VuIE1SQ0Egbm9kZSAoNzI3KSBoYXMgMzkgY2hpbGRyZW4gYmFzZWQgb24gdGhlIG51bWJlciBvZiB0aXAgbGFiZWxzIGluIG91ciBgdHJlZWRhdGFgIG9iamVjdC4gV2UgY2FuIGFsc28gbm93IHVzZSB0aGlzIE1SQ0EgdG8gZGV0ZXJtaW5lIHRoZSBzcGVjaWZpYyBjbGFkZSB3ZSBjYW4gc2VlIG9uIHRoZSB0cmVlIGJ1dCB0aGlzIHdpbGwgc3RpbGwgYmUgcXVpdGUgbGFyZ2UuIFRvIHZpZXcgdGhpcyBwYXJ0aWN1bGFyIGNsYWRlLCB3ZSBidWlsZCBvdXIgcGxvdCBhcyBiZWZvcmUsIGFuZCB0aGVuIHpvb20gaW50byBpdCBhZnRlcndhcmRzIHdpdGggdGhlIGB2aWV3Q2xhZGUoKWAgZnVuY3Rpb24uDQoNClVuZm9ydHVuYXRlbHksIHdlJ2xsIGhhdmUgdG8gZGl0Y2ggb3VyIGRhdGUgc2NhbGUgYW5kIHJldmVydCB0byBhIGRlY2ltYWwtZGF0ZS4gSWYgeW91J3JlIG5vdCB1c2luZyBhIGNhbGVuZGFyLWJhc2VkIGRhdGUsIGl0J3Mgbm90IGEgcHJvYmxlbSBhdCBhbGwuIElmIHdlIGxvb2sgY2FyZWZ1bGx5IGF0IHRoZSBkYXRhLCB3ZSdsbCBzZWUgdGhhdCB0aGUgaW50ZXJuYWwgbm9kZXMgaGF2ZSBhbiBOQS12YWx1ZS4gSWYgeW91IHdlcmUgKmluZHVzdHJpb3VzIGVub3VnaCosIHlvdSBjb3VsZCB1c2UgdGhlIGJyYW5jaCBsZW5ndGhzIHRvIHRyYXZlcnNlIHRoZSB0cmVlIGFuZCBjYWxjdWxhdGUgZGF0ZXMgZm9yIGFsbCB0aGUgaW50ZXJuYWwgbm9kZXMgYXMgd2VsbC4gVGhpcyB3b3VsZCBsaWtlbHkgc29sdmUgdGhlIGlzc3VlLg0KDQpMZXQncyBkcm9wIHRoZSB0aXAgbmFtZXMgYmFjayBpbiB0aGVyZSB0b28uDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9DQojIFNob3cgYSBzbWFsbCBjbGFkZSBvZiB0aGUgdHJlZQ0KDQp0cmVlLnBsb3QgPC0NCg0KU0MyX3ZhcmlhbnRzX3RpbWUudHJlZSAlPiUNCiAgIyBGcm9tIG91ciB0cmVlLCBkcm9wIHRoZSBOb24tQ2FuYWRpYW4gdGlwcw0KICBkcm9wLnRpcCguLCB0b19kcm9wKSAlPiUgDQogIA0KICAjIDEuIERhdGENCiAgZ2d0cmVlKHRyID0gLiwgbXJzZCA9ICIyMDIxLTAyLTE2IikgKyAjIE5lZWQgdGhlIG1vc3QgcmVjZW50IHNhbXBsaW5nIGRhdGUNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoY29sb3IgPSBlbWVyZ2luZ19uZXh0c3RyYWluKSArIA0KICAgIA0KICAgICMgVGhlbWVzDQogICAgdGhlbWVfdHJlZTIoKSArIA0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMwKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgIyBNb3ZlIG91ciBsZWdlbmQgdG8gdGhlIGJvdHRvbQ0KICAgICAgICAgIGxlZ2VuZC5ib3ggPSAidmVydGljYWwiKSArICAjIFN0YWNrIGxlZ2VuZHMgdmVydGljYWxseQ0KDQogICAgIyBQcm92aWRlIHNvbWUgbGFiZWxzDQogICAgbGFicyh4PSJUaW1lIiwNCiAgICAgICAgIHRpdGxlID0gIlN1YnNldCBvZiBDYW5hZGlhbiBzZXF1ZW5jZWQgc3RyYWlucyIpICsNCiAgICANCiAgICAjIE1ha2Ugb3VyIGd1aWRlIGxpbmVzIGEgbGl0dGxlIHRoaWNrZXIgDQogICAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJOZXh0c3RyYWluXG5jbGFkZSIsICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0yKSksDQogICAgICAgICAgIHNoYXBlID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIlN0cmFpblxubG9jYXRpb24iKQ0KICAgICAgICAgICkgKyANCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDE1OjIwLCAxMSkpICsNCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX3RpcGxhYihzaXplID0gNywgYWxpZ249VFJVRSkgKyAjIEFkZCBsYWJlbHMgaW4gYW5kIHJpZ2h0LWFsaWduIHRoZW0gDQoNCiAgICAjIEFkZCB0aXBzIGxhYmVscyB0byBvdXIgdHJlZSB0aXBzDQogICAgIyBDaGFuZ2Ugb3VyIHRpcCBzaGFwZSBieSBzdHJhaW5fZGl2aXNpb24NCiAgICBnZW9tX3RpcHBvaW50KGFlcyhzaGFwZSA9IHN0cmFpbl9kaXZpc2lvbiksIHNpemUgPSAzKSAgIyBBZGQgdGlwcyBvbmx5ICAgDQoNCiMjIyAxLjcuMSBWaWV3IHRoZSBjbGFkZSB3ZSBtYWRlIHVzaW5nIHRoZSBzYW1lIE1SQ0EgY2FsbCB3ZSBhbHJlYWR5IHVzZWQuDQp2aWV3Q2xhZGUodHJlZS5wbG90LCBNUkNBKHRyZWUucGxvdCwgdG9fdmlld1sxOjVdKSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMS44LjAgTWFrZSBhIG5ldyBzdWJwbG90IHdpdGggaW5mb3JtYXRpb24gZnJvbSBhY3Jvc3MgYWxsIGNsYWRlcw0KDQpTbyB3ZSd2ZSBsb29rZWQgYXQgYSBmZXcgd2F5cyB0byBzdWJzZXQgYW5kIHBsb3Qgb3VyIHBoeWxvZ2VuZXRpYyB0cmVlLiBXZSdsbCBkbyBhIHF1aWNrIGFzaWRlIHRvIGNyZWF0ZSBhIG1vcmUgY3VyYXRlZCBsaXN0IG9mIHRpcHMgd2l0aCByZXByZXNlbnRhdGlvbiBmcm9tIGFjcm9zcyBtdWx0aXBsZSBjbGFkZXMuIFdlJ2xsIHVzZSB0aGlzIGFzIHRoZSBiYXNlIHRyZWUgZm9yIG91ciBuZXh0IGZldyBncmFwaHMgc28gdGhhdCB3ZSBjYW4gcmVhbGx5IHNlZSB0aGUgcG93ZXIgb2YgdXNpbmcgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBmcm9tIG91ciBgdHJlZWRhdGFgIG9iamVjdC4NCg0KYGBge3J9DQojIEhlcmUncyBvdXIgcmVwcmVzZW50YXRpdmUgbGlzdCBvZiB0aXBzIGZyb20gbXVsdGlwbGUgY2xhZGVzDQoNCmN1cmF0ZWRfdGlwcyA8LQ0KYygnV3VoYW4vV0gwMS8yMDE5JywNCidDYW5hZGEvT05fT04tVklETy0wMS0yLzIwMjAnLA0KJ0NhbmFkYS9PTl9WSURPLTAxLzIwMjAnLA0KJ0NhbmFkYS9CQ18zN18wLTIvMjAyMCcsDQonQ2FuYWRhL0JDXzY5MjQzLzIwMjAnLA0KJ0NhbmFkYS9RQy1DSFVNLTIwMDgwMDM5NTZBLzIwMjAnLA0KJ0NhbmFkYS9OTC1OTUwtMTM4Ny8yMDIwJywNCidDYW5hZGEvTUItTk1MLTgwOC8yMDIwJywNCidDYW5hZGEvTkItTk1MLTE2OTY3LzIwMjEnLA0KJ0NhbmFkYS9CQ182MTI5MTI3LzIwMjAnLA0KJ0NhbmFkYS9BQi05Nzc3Ni8yMDIwJywNCidDYW5hZGEvQUItMTI5NDgvMjAyMCcsDQonQ2FuYWRhL0JDLUJDQ0RDLTM1NjQvMjAyMCcsDQonQ2FuYWRhL09OLVM2Ny8yMDIwJywNCidDYW5hZGEvQUItNjUyMzMvMjAyMCcsDQonQ2FuYWRhL01CLU5NTC0xMDU3LzIwMjAnLA0KJ0NhbmFkYS9PTi1VSFRDLTAzNjYvMjAyMCcsDQonQ2FuYWRhL05TLU5NTC0xNjIxOC8yMDIxJywNCidDYW5hZGEvTkItTk1MLTE2OTY2LzIwMjEnLA0KJ0NhbmFkYS9OU18xMy8yMDIwJywNCidDYW5hZGEvUWMtQ0hVTS0yMDE5MjAzODU2QS8yMDIwJywNCidDYW5hZGEvTkItTk1MLTMyNzIvMjAyMCcsDQonQ2FuYWRhL09OLVVIVEMtMDI2Ny8yMDIwJywNCidDYW5hZGEvTUItTk1MLTEwMzcvMjAyMCcsDQonQ2FuYWRhL05TLU5NTC01MjEzLzIwMjAnLA0KJ0NhbmFkYS9OUy1OTUwtNTE0MS8yMDIxJywNCidDYW5hZGEvTlMtTk1MLTUxNDAvMjAyMScsDQonQ2FuYWRhL05TLU5NTC0xNjIwOC8yMDIxJywNCidDYW5hZGEvQkMtQkNDREMtNzAxMi8yMDIwJywNCidDYW5hZGEvT04tTFRSSS0xMzcyLzIwMjAnLA0KJ0NhbmFkYS9CQy1CQ0NEQy05NzM2LzIwMjEnLA0KJ0NhbmFkYS9PTi1TMjEyNS8yMDIxJywNCidDYW5hZGEvTlMtTk1MLTUxODEvMjAyMCcsDQonQ2FuYWRhL09OLVBQUy0yMTAxMjAyMV8wMjY5LzIwMjEnLA0KJ0NhbmFkYS9RQy1MMDAzMTQ1MzkvMjAyMCcsDQonQ2FuYWRhL05TLU5NTC0xNjIzOC8yMDIxJywNCidDYW5hZGEvTkwtTk1MLTE2ODIyLzIwMjEnLA0KJ0NhbmFkYS9RQy1MMDAzMjQ1ODkwMDEvMjAyMScsDQonQ2FuYWRhL05MLU5NTC0xNzE5NC8yMDIxJykNCmBgYA0KDQpgYGB7cn0NCiMgTWFrZSBhIG5ldyBsaXN0IG9mIHRpcHMgdG8gZHJvcA0KY3VyYXRlZF9kcm9wX2xpc3QgPC0NCiAgU0MyX21ldGFkYXRhLmRmICU+JSANCg0KICAjIGZpbHRlciB0aGUgb3Bwb3NpdGUgb2Ygb3VyIHRpcCBsYWJlbHMNCiAgZmlsdGVyKCEoU3RyYWluICVpbiUgY3VyYXRlZF90aXBzKSkgJT4lIA0KICAjIE9ubHkga2VlcCB0aGUgc3RyYWluIGluZm9ybWF0aW9uIGZvciB3aGF0IHdlIHdhbnQgdG8gZHJvcA0KICBwdWxsKFN0cmFpbikNCmBgYA0KDQpgYGB7cn0NCiMgRHJvcCB0aXBzIGZyb20gb3VyIHRyZWUgYW5kIHNhdmUgaXQgYXMgY3VyYXRlZF90cmVlX2luZm8uZGYNCmN1cmF0ZWRfdHJlZV9pbmZvLmRmIDwtDQoNCiAgIyBQYXNzIGFsb25nIG91ciBvcmlnaW5hbCB0cmVlDQogIFNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lDQoNCiAgIyBEcm9wIHRoZSB0aXBzIHdlIGNyZWF0ZWQNCiAgZHJvcC50aXAoLiwgLi4uKSAlPiUgDQoNCiAgIyBDb252ZXJ0IHRvIGEgdGliYmxlDQogIGFzX3RpYmJsZSgpICU+JSANCg0KICAjIFJlbmFtZSBvdXIgY29sdW1uIGluZm9ybWF0aW9uDQogIHJlbmFtZShDbGFkZSA9IC4uLiwgUHJvdmluY2UgPSAuLi4pICU+JSANCg0KICBtdXRhdGUoQWdlID0gcmVwbGFjZV9uYShBZ2UsIHJlcGxhY2UgPSAwKSkgJT4lIA0KDQogICMgQ29udmVydCB0aGlzIHRvIGEgZGF0YSBmcmFtZQ0KICBhcy5kYXRhLmZyYW1lKCkNCg0KIyBTZXQgdGhlIHJvd25hbWVzIGZyb20gdGhlIGxhYmVsIGluZm9ybWF0aW9uDQpyb3duYW1lcyhjdXJhdGVkX3RyZWVfaW5mby5kZikgPC0gY3VyYXRlZF90cmVlX2luZm8uZGYkbGFiZWwNCg0KIyBMZXQncyB2aWV3IHRoZSB0cmVlDQpzdHIoY3VyYXRlZF90cmVlX2luZm8uZGYpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuOS4wIENvbnZlcnQgeW91ciB0cmVlIHN0eWxlIHdpdGggdGhlIGBsYXlvdXRgIHBhcmFtZXRlcg0KDQpTbyB3ZSd2ZSBvbmx5IGJlZW4gd29ya2luZyB3aXRoIGEgcmVjdGFuZ3VsYXIgdHJlZSAoaG9yaXpvbnRhbCBsYXlvdXQpIHRodXMgZmFyIGJ1dCBhcyB3ZSBtZW50aW9uZWQgYXQgdGhlIGJlZ2lubmluZyB0aGVyZSBhcmUgYWN0dWFsbHkgYSBudW1iZXIgb2YgZGlmZmVyZW50IGxheW91dHMgd2UgY2FuIHVzZS4gT3VyIGdvYWwgbm93IGlzIHRvIG1ha2UgYSBzb3J0IG9mIHN1bmJ1cnN0IHBsb3QgdXNpbmcgYSBjaXJjdWxhciBsYXlvdXQgdGhhdCB3ZSdsbCBhZGQgdmlzdWFsIGNhdGVnb3JpY2FsIGluZm9ybWF0aW9uIHRvIGluIGEgcmluZyBwYXR0ZXJuLg0KDQpGaXJzdCwgd2UnbGwgc3RhcnQgd2l0aCB0aGUgY2lyY3VsYXIgdmVyc2lvbi4gV2UnbGwgY29sb3VyIG91ciB0aXBzIGJ5IHRoZSBwcm92aW5jZSB3aGVyZSB0aGUgY2FzZSBvciBkYXRhIHdhcyBnZW5lcmF0ZWQgYW5kIGxhYmVsIHRoZW0gYnkgdGhlIE5leHRzdHJhaW4gY2xhZGUgaW5mb3JtYXRpb24uDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9DQojIFNob3cgb3VyIGN1cmF0ZWQgdHJlZSBpbiBhIGNpcmN1bGFyIGZvcm1hdA0KDQp0cmVlLnBsb3QgPC0NCg0KICBTQzJfdmFyaWFudHNfdGltZS50cmVlICU+JQ0KICBkcm9wLnRpcCguLCBjdXJhdGVkX2Ryb3BfbGlzdCkgICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3RyZWUoLiwgbGF5b3V0ID0gLi4uLCAjIyMgMS45LjAgU2V0IHRoZSBsYXlvdXQgdG8gY2lyY3VsYXINCiAgICAgICAgIG1yc2QgPSAiMjAyMS0wMi0xNyIpICsgIyBOZWVkIHRoZSBtb3N0IHJlY2VudCBzYW1wbGluZyBkYXRlDQogICAgIyAyLiBBZXN0aGV0aWNzDQoNCiAgICAjIFRoZW1lcw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgIGxhYnModGl0bGUgPSAiQ2FuYWRhIGFuZCBVUyBzZXF1ZW5jZWQgc3RyYWluIHBoeWxvZ2VueSIsDQogICAgICAgICBjb2xvdXIgPSAiUHJvdmluY2UiKSArDQoNCiAgICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKG9wdGlvbiA9ICJwbGFzbWEiKSArICMgVGhpcyBzZWVtcyB0byBsaXRlcmFsbHkga2lsbCB0aGUga2VybmVsIQ0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91ciA9IHN0cmFpbl9kaXZpc2lvbiksIHNpemUgPSA1KSArIA0KDQogICAgIyMjIDEuOS4wIHNldCB0aGUgdGlwIGxhYmVscw0KICAgIGdlb21fdGlwbGFiKGFlcyhsYWJlbCA9IC4uLiksIHNpemUgPSA4KQ0KDQojIFZpZXcgb3VyIHRyZWUNCnRyZWUucGxvdA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjEwLjAgVXNlIGBnaGVhdG1hcCgpYCBsYXllciB0byBhZGQgaW5mb3JtYXRpb24gdG8geW91ciBwbG90DQoNCk5vdyB0aGF0IHdlIGhhdmUgb3VyIGJhc2ljIGNpcmN1bGFyIHRyZWUsIHdlIGNhbiB1c2UgdGhlIGluZm9ybWF0aW9uIGZyb20gYGN1cmF0ZWRfdHJlZV9pbmZvLmRmYCB0byB2aXN1YWxpemUgYWRkaXRpb25hbCBkYXRhIG9uIG91ciB0cmVlLiBJbiBvcmRlciB0byB1c2UgdGhlIGBnaGVhdG1hcGAgbGF5ZXIgcHJvcGVybHksIHdlIG11c3QgcGFzcyBpdCBvdXIgb3JpZ2luYWwgdHJlZSBwbG90LCBhbG9uZyB3aXRoIGEgbmV3IGRhdGFmcmFtZSAob3IgdmVjdG9yKSB0aGF0IGhvbGRzIHRoZSBpbmZvcm1hdGlvbiB3ZSB3YW50IHRvIGFkZC4gSXQgc2hvdWxkIHVzZSB0aGUgcm93bmFtZXMgZnJvbSB0aGUgZGF0YSBmcmFtZSB0byBoZWxwIG1hdGNoIHVwIHRvIHRoZSB0aXBzIGluIG91ciB0cmVlLg0KDQpXZSdsbCBkcm9wIHRoZSB0aXAgbGFiZWxzIGZyb20gb3VyIHRyZWUgaW4gZmF2b3VyIG9mIHRoZSBoZWF0bWFwIHdlJ2xsIGFkZC4gQnkgYWRkaW5nIGxheWVycyBvZiBoaWVyYXJjaGljYWwgZGF0YSwgdGhpcyBjcmVhdGVzIHdoYXQgaXMga25vd24gYXMgYSAqKlN1bmJ1cnN0KiogcGxvdC4NCg0KVGhlIGdoZWF0bWFwKCkgbGF5ZXIgdGFrZXMgc29tZSBvZiB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnM6DQoNCi0gICBgcGA6IHRoZSB0cmVlIHBsb3Qgd2Ugd2FudCB0byBtb2RpZnkuDQoNCi0gICBgZGF0YWA6IHRoZSBkYXRhIHVzZWQgdG8gbW9kaWZ5IHRoZSB0cmVlIHBsb3QgYHBgLg0KDQotICAgYG9mZnNldGA6IGRldGVybWluZXMgdGhlIG9mZnNldCBvZiB0aGUgaGVhdG1hcCByZWxhdGl2ZSB0byB0aGUgdHJlZS4NCg0KLSAgIGB3aWR0aGA6IGRldGVybWluZXMgdGhlIHRvdGFsIHdpZHRoIG9mIHRoZSBoZWF0bWFwcyBjb21wYXJlZCB0byB0aGUgdHJlZQ0KDQotICAgYGNvbG5hbWVzX2FuZ2xlYDogZGV0ZXJtaW5lcyB0aGUgYW5nbGUgb2YgdGhlIHRleHQgZm9yIHRoZSBoZWF0bWFwLg0KDQotICAgYGZvbnQuc2l6ZWA6IHNldHMgdGhlIGZvbnQgc2l6ZSBmb3IgdGhlIGhlYXRtYXAgcG9ydGlvbiBvZiB0aGUgdHJlZS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0NCiMgU2hvdyBvdXIgY3VyYXRlZCB0cmVlIGluIGEgY2lyY3VsYXIgZm9ybWF0DQoNCnRyZWUucGxvdCA8LQ0KDQogIFNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lDQogIGRyb3AudGlwKC4sIGN1cmF0ZWRfZHJvcF9saXN0KSAgJT4lIA0KDQogICMgMS4gRGF0YQ0KICBnZ3RyZWUoLiwgbGF5b3V0ID0gImNpcmN1bGFyIiwgbXJzZCA9ICIyMDIxLTAyLTE3IikgKyAjIE5lZWQgdGhlIG1vc3QgcmVjZW50IHNhbXBsaW5nIGRhdGUNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICANCiAgICAjIFRoZW1lcw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgIGxhYnModGl0bGUgPSAiQ2FuYWRhIGFuZCBVUyBzZXF1ZW5jZWQgc3RyYWluIHBoeWxvZ2VueSIsDQogICAgICAgICBjb2xvdXIgPSAiUHJvdmluY2UiKSArDQoNCiAgICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKG9wdGlvbiA9ICJwbGFzbWEiKSArDQoNCiAgICAjIDQuIEdlb21zDQogICAgZ2VvbV90aXBwb2ludChhZXMoY29sb3VyID0gc3RyYWluX2RpdmlzaW9uKSwgc2l6ZSA9IDUpICMgQWRkIHRpcHMgb25seSANCg0KIyMjIDEuMTAuMCBBZGQgYSBjaXJjdWxhciBoZWF0bWFwIHdpdGggZ2hlYXRtYXAoKQ0KdHJlZS5wbG90IDwtIC4uLih0cmVlLnBsb3QsIC4uLiwgDQogICAgICAgICAgICAgICAgICAgICAgb2Zmc2V0ID0gMC4xLCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDkwLCBmb250LnNpemUgPSA2LCBoanVzdCA9IDEpICsgDQogIHNjYWxlX2ZpbGxfY29udGludW91cyhuYW1lID0gIlRyZWUgY29kaW5nIikgDQoNCg0KdHJlZS5wbG90DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjEwLjEgYGdnbmV3c2NhbGVgIHBhY2thZ2UgYWxsb3dzIG11bHRpcGxlIGNvbG9yL2ZpbGwgc2NhbGVzIG9uIHlvdXIgU3VuYnVyc3QgcGxvdA0KDQpGcm9tIG91ciBzdW5idXJzdCBwbG90IGFib3ZlIHdlIHVzZWQgKm11bHRpcGxlKiBjb2x1bW5zIGZyb20gYGN1cmF0ZWRfdHJlZV9pbmZvLmRmYCBidXQgdGhlIGNhdmVhdCBpcyB0aGF0IHRoZSBsZWdlbmQgZm9yIGVhY2ggcmVzdWx0aW5nIGNvbHVtbiBjb21iaW5lZCBhbGwgb2YgdGhlIGRhdGEgaW50byBhIHNpbmdsZSBuZXcgbGVnZW5kLiBUaGF0J3MgaGVscGZ1bCBpZiB0aGUgKnR5cGUqIG9mIGRhdGEgY291bGQgYmUgY29udGludW91cyB2YWx1ZXMgbGlrZSBhIFswLDFdIHNjYWxlIGhlYXRtYXAgY29sb3VyIGFjcm9zcyBkaWZmZXJlbnQgZmVhdHVyZXMuIFdoZW4gZGVhbGluZyB3aXRoIG11bHRpcGxlIGNhdGVnb3JpZXMgd2hlcmUgdGhlIHNjYWxlIG9mIHRoZSB2YWx1ZXMgdmFyaWVzLCBob3dldmVyLCB0aGF0IGRvZXNuJ3Qgd29yayB3ZWxsIGZvciB1cy4gVGhpcyBhbHNvIGRvZXNuJ3Qgd29yayB3ZWxsIGZvciBjYXRlZ29yaWNhbCBkYXRhLg0KDQpJbnN0ZWFkLCB3ZSdkIGxpa2UgdG8gc2VwYXJhdGUgdGhlIGNvbG91ci9maWxsIHNjYWxlcyBieSByZXBlYXRlZGx5IGFkZGluZyB0byBvdXIgcGxvdCwgbGF5ZXIgYnkgbGF5ZXIuIEFzIHlvdSBtaWdodCByZWNhbGwsIGhvd2V2ZXIsIHdlIGFsc28gcnVuIGludG8gdGhlIHByb2JsZW0gb2YgYHNjYWxlX2NvbG91cl8qYCBhbmQgYHNjYWxlX2ZpbGxfKmAgbGF5ZXJzIG92ZXJ3cml0aW5nIGFueSAqcHJldmlvdXMqIGxheWVycy4NCg0KVG8gY2lyY3VtdmVudCB0aGlzIHJlYWxpdHksIHdlJ2xsIHVzZSB0aGUgYGdnbmV3c2NhbGVgIHBhY2thZ2UgdG8gZ2VuZXJhdGUgbmV3IGNvbG91ciBhbmQgZmlsbCBzY2FsZXMgd2l0aCBgbmV3X3NjYWxlX2ZpbGwoKWAgYW5kIGBuZXdfc2NhbGVfY29sb3VyKClgLiBJdCBjYW4gbWFrZSB0aGUgcHJvY2VzcyBzbGlnaHRseSBtb3JlIGVuY3VtYmVyaW5nIGJ1dCBpdCB3aWxsIHdvcmsgb3V0Lg0KDQpXZSdsbCBhZGQgYmFjayBpbiBvdXIgb3JpZ2luYWwgdGlwIGxhYmVscyBhcyB3ZWxsIGZvciB0aGlzIGdyYXBoLg0KDQpgYGB7ciwgZmlnLndpZHRoPTQ1LCBmaWcuaGVpZ2h0PTQ1fQ0KDQojIFNob3cgb3VyIGN1cmF0ZWQgdHJlZSBpbiBhIGNpcmN1bGFyIGZvcm1hdA0KDQojIFdlJ2xsIG5lZWQgc29tZSBleHRyYSBjb2xvdXIgc2V0cyB0byBhY2NvbXBsaXNoIHRoaXMgcGxvdA0KY29tYm8uY29sb3VycyA9IGMoYnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpLCBicmV3ZXIucGFsKDgsICJTZXQxIiksIGJyZXdlci5wYWwoMTIsICJTZXQzIikpDQoNCg0KIyBTYXZlIG91ciBmaXJzdCBwbG90IHdpdGgganVzdCB0aGUgdHJlZQ0KdHJlZS5wbG90IDwtDQoNCiAgU0MyX3ZhcmlhbnRzX3RpbWUudHJlZSAlPiUNCiAgZHJvcC50aXAoLiwgY3VyYXRlZF9kcm9wX2xpc3QpICAlPiUgDQogIA0KICAjIDEuIERhdGENCiAgZ2d0cmVlKC4sIGxheW91dCA9ICJjaXJjdWxhciIsIG1yc2QgPSAiMjAyMS0wMi0xNyIpICsgIyBOZWVkIHRoZSBtb3N0IHJlY2VudCBzYW1wbGluZyBkYXRlDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgDQogICAgIyBUaGVtZXMNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMCksDQogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICBsYWJzKHRpdGxlID0gIkNhbmFkYSBhbmQgVVMgc2VxdWVuY2VkIHN0cmFpbiBwaHlsb2dlbnkiKSArDQoNCiAgICAjIFdlJ2xsIHdhbnQgdG8gZW5zdXJlIG91ciBjb2xvdXIgZ3VpZGUgZW5kcyB1cCBpbiB0aGUgY29ycmVjdCBvcmRlcg0KICAgIHNjYWxlX2NvbG91cl9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUHJvdmluY2UiLCBvcmRlcj0xKSkgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91ciA9IHN0cmFpbl9kaXZpc2lvbiksIHNpemUgPSA1KSArICMgQWRkIHRpcHMgb25seSANCiAgICBnZW9tX3RpcGxhYihzaXplID0gMTAsIGFsaWduPVRSVUUsIG9mZnNldCA9IDAuNikgIyBBZGQgaW4gb3VyIGxhYmVscw0KDQoNCiMgR2VuZXJhdGUgb3VyIGZpcnN0IGhlYXRtYXAgbGF5ZXINCnRyZWUucGxvdCA8LSBnaGVhdG1hcCh0cmVlLnBsb3QsIGN1cmF0ZWRfdHJlZV9pbmZvLmRmICU+JSBzZWxlY3QocGFyZW50LCBub2RlLCBicmFuY2gubGVuZ3RoKSwgDQogICAgICAgICAgICAgICAgICAgICAgb2Zmc2V0ID0gMC4xLCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDkwLCBmb250LnNpemUgPSA2KSArIA0KICAjIFNldCB0aGUgbmFtZSBvZiBvdXIgbGVnZW5kDQogIHNjYWxlX2ZpbGxfY29udGludW91cyhuYW1lID0gIlRyZWUgY29kaW5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpIA0KDQojIyMgMS4xMC4xIHVzZSB0aGlzIGNvZGUgdG8gY3JlYXRlIGEgbmV3IGNvbG91ciBzY2FsZQ0KdHJlZS5wbG90IDwtIHRyZWUucGxvdCArIC4uLiANCg0KIyBHZW5lcmF0ZSBhIGNhdGVnb3JpY2FsIGhlYXRtYXAgbGF5ZXIgZm9yIHRoZSBDbGFkZSB2YXJpYWJsZQ0KdHJlZS5wbG90IDwtIGdoZWF0bWFwKHRyZWUucGxvdCwgY3VyYXRlZF90cmVlX2luZm8uZGYgJT4lIHNlbGVjdChDbGFkZSksIA0KICAgICAgICAgICAgICAgICAgICAgIG9mZnNldCA9IDAuMiwgd2lkdGggPSAwLjEsDQogICAgICAgICAgICAgICAgICAgICAgY29sbmFtZXNfYW5nbGUgPSA5MCwgZm9udC5zaXplID0gMTIpICsgDQogICAgDQogICMgU2V0IHRoZSBuYW1lIGFuZCBvcmRlciBvZiBvdXIgQ2xhZGUgbGVnZW5kDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJOZXh0c3RyYWluXG5DbGFkZSIsIA0KICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyPTMpKQ0KDQojIyMgMS4xMC4xIHVzZSB0aGlzIGNvZGUgdG8gY3JlYXRlIGEgbmV3IGNvbG91ciBzY2FsZQ0KdHJlZS5wbG90IDwtIHRyZWUucGxvdCArIC4uLiANCg0KIyBHZW5lcmF0ZSBhIGNhdGVnb3JpY2FsIGhlYXRtYXAgbGF5ZXIgZm9yIHRoZSBQQU5HTyB2YXJpYWJsZQ0KdHJlZS5wbG90IDwtIGdoZWF0bWFwKHRyZWUucGxvdCwgY3VyYXRlZF90cmVlX2luZm8uZGYgJT4lIHNlbGVjdCguLi4pLCANCiAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSAwLjM1LCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDkwLCBmb250LnNpemUgPSAxMikgKyAgICAgDQoNCiAgIyBTZXQgdGhlIG5hbWUgYW5kIG9yZGVyIG9mIG91ciBQQU5HTyBsZWdlbmQNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29tYm8uY29sb3VycywgbmFtZSA9ICJQQU5HT1xubGluZWFnZSIsIA0KICAgICAgICAgICAgICAgICAgICBndWlkZT1ndWlkZV9sZWdlbmQob3JkZXI9NCkpDQoNCiMgWW91IG1heSBuZWVkIHRvIHJlLXJlbmRlciB0byBzZWUgaXQgcHJvcGVybHkgKEV4cGFuZC9jb2xsYXBzZSBhZ2FpbikNCnRyZWUucGxvdA0KYGBgDQoNCiMjIyAxLjEwLjIgQ3JlYXRlIGEgdmVydGljYWwgdmVyc2lvbiBvZiBvdXIgcGxvdCB1c2luZyBhIHJlY3Rhbmd1bGFyIGxheW91dA0KDQpEZXBlbmRpbmcgb24gdGhlIGxldmVsIG9mIGRhdGEgZGVwdGgsIHlvdSBtYXkgYWxzbyBjaG9vc2UgdG8gbGF5ZXIgeW91ciBzY2FsZXMgYWxvbmcgdGhlIHJpZ2h0LWhhbmQgc2lkZSBvZiB5b3VyIHRyZWUuIFRvIGFjY29tcGxpc2ggdGhpcyB3ZSBjYW4gc2ltcGx5IHNldCBvdXIgYGxheW91dGAgcGFyYW1ldGVyIHRvICJyZWN0YW5ndWxhciIuIEF0IHRoZSBzYW1lIHRpbWUsIHdlJ2xsIHJ1biBpbnRvIGEgY291cGxlIG9mIHByb2JsZW1zIHdpdGggaG93IHRoZSBgZ2d0cmVlYCBpcyBtYWRlLg0KDQpPbmUgaXNzdWUgd2l0aCBnZ3Bsb3QgaXMgdGhhdCBhcyB3ZSBhZGQgdGhlIHNjYWxlcywgdGhlc2UgYXJlIHZlcnkgbXVjaCBsaWtlIGFubm90YXRpb25zIG9uIG91ciBnZ3Bsb3QuIFRoZSB2ZXJ0aWNhbCAoYW5kIGhvcml6b250YWwpIGRpc3BsYXkgYXJlYXMgb2Ygb3VyIHBsb3QgYXJlIG5vdCB1cGRhdGVkIGFzIHdlIGFkZCB0aGVzZSBhbm5vdGF0aW9ucy4gV2hlbiB3ZSBhZGQgbmV3IGNvbG91ciBzY2FsZSBsYXllcnMgdG8gdGhlIHBsb3QsIHRoZSBsYWJlbHMgd2lsbCwgaW4gbWFueSBjYXNlcywgYmxlZWQgb3V0c2lkZSBvZiB0aGUgZGVzaWduYXRlZCBwbG90IGFyZWEuIFRvIGNvbWJhdCB0aGlzLCB3ZSBjYW4gYWRkIGEgbGF5ZXIgY2FsbGVkIGB2ZXhwYW5kKClgIG9yIGBoZXhwYW5kKClgIGZvciBob3Jpem9udGFsIGV4cGFuc2lvbi4gQm90aCBvZiB0aGVzZSB0YWtlIDIgcGFyYW1ldGVyczoNCg0KLSAgIGByYXRpb2A6IHRoZSByYXRpbyBvZiBleHBhbnNpb24gZm9yIHlvdXIgcGxvdCBheGlzDQotICAgYGRpcmVjdGlvbmA6IHRoZSBkaXJlY3Rpb24geW91IHdhbnQgdGhlIGV4cGFuc2lvbiBpbiBhIHJhbmdlIGZyb20gMSAobGVmdC90b3ApIHRvIC0xIChyaWdodC9ib3R0b20pLg0KDQpgYGB7ciwgZmlnLndpZHRoPTMwLCBmaWcuaGVpZ2h0PTM1fQ0KDQojIFNob3cgb3VyIGN1cmF0ZWQgdHJlZSBpbiBhIHJlY3Rhbmd1bGFyIGZvcm1hdA0KDQojIFdlJ2xsIG5lZWQgc29tZSBleHRyYSBjb2xvdXIgc2V0cyB0byBhY2NvbXBsaXNoIHRoaXMgcGxvdA0KY29tYm8uY29sb3VycyA9IGMoYnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpLCBicmV3ZXIucGFsKDgsICJTZXQxIiksIGJyZXdlci5wYWwoMTIsICJTZXQzIikpDQoNCiMgU2F2ZSBvdXIgZmlyc3QgcGxvdCB3aXRoIGp1c3QgdGhlIHRyZWUNCnRyZWUucGxvdCA8LQ0KDQogIFNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lDQogIGRyb3AudGlwKC4sIGN1cmF0ZWRfZHJvcF9saXN0KSAgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdndHJlZSguLCBsYXlvdXQgPSAuLi4sICMjIyAxLjEwLjIgY29udmVydCB5b3VyIHBsb3QgdG8gYSByZWN0YW5ndWxhciBmb3JtYXQgDQogICAgICAgICBtcnNkID0gIjIwMjEtMDItMTciKSArICMgTmVlZCB0aGUgbW9zdCByZWNlbnQgc2FtcGxpbmcgZGF0ZQ0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIA0KICAgICMgVGhlbWVzDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzApLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgICAgKSArDQogICAgbGFicyh0aXRsZSA9ICJDYW5hZGEgYW5kIFVTIHNlcXVlbmNlZCBzdHJhaW4gcGh5bG9nZW55IiwNCiAgICAgICAgIGNvbG91ciA9ICJQcm92aW5jZSIpICsNCg0KICAgICMjIyAxLjEwLjIgRXhwYW5kIHRoZSB2ZXJ0aWNhbCBwbG90IHNpemUNCiAgICB2ZXhwYW5kKHJhdGlvID0gLi4uLCBkaXJlY3Rpb24gPSAuLi4pICsgDQoNCiAgICAjIFdlJ2xsIHdhbnQgdG8gZW5zdXJlIG91ciBjb2xvdXIgZ3VpZGUgZW5kcyB1cCBpbiB0aGUgY29ycmVjdCBvcmRlcg0KICAgIHNjYWxlX2NvbG91cl9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUHJvdmluY2UiLCBvcmRlcj0xKSkgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91ciA9IHN0cmFpbl9kaXZpc2lvbiksIHNpemUgPSA1KSArICMgQWRkIHRpcHMgb25seSANCiAgICBnZW9tX3RpcGxhYihzaXplID0gMTAsIGFsaWduPVRSVUUpICMgQWRkIGluIG91ciBsYWJlbHMgDQoNCg0KIyBHZW5lcmF0ZSBvdXIgZmlyc3QgaGVhdG1hcCBsYXllcg0KdHJlZS5wbG90IDwtIGdoZWF0bWFwKHRyZWUucGxvdCwgY3VyYXRlZF90cmVlX2luZm8uZGYgJT4lIHNlbGVjdChwYXJlbnQsIG5vZGUsIGJyYW5jaC5sZW5ndGgpLCANCiAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSAwLjc1LCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDkwLCBmb250LnNpemUgPSA4LCBoanVzdCA9IDEpICsgDQogICMgU2V0IHRoZSBuYW1lIG9mIG91ciBsZWdlbmQNCiAgc2NhbGVfZmlsbF9jb250aW51b3VzKG5hbWUgPSAiVHJlZSBjb2RpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgDQoNCiMgdXNlIHRoaXMgY29kZSB0byBjcmVhdGUgYSBuZXcgY29sb3VyIHNjYWxlDQp0cmVlLnBsb3QgPC0gdHJlZS5wbG90ICsgbmV3X3NjYWxlX2ZpbGwoKSANCg0KIyBHZW5lcmF0ZSBhIGNhdGVnb3JpY2FsIGhlYXRtYXAgbGF5ZXIgZm9yIHRoZSBDbGFkZSB2YXJpYWJsZQ0KdHJlZS5wbG90IDwtIGdoZWF0bWFwKHRyZWUucGxvdCwgY3VyYXRlZF90cmVlX2luZm8uZGYgJT4lIHNlbGVjdChDbGFkZSksIA0KICAgICAgICAgICAgICAgICAgICAgIG9mZnNldCA9IDAuODUsIHdpZHRoID0gMC4xLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzX2FuZ2xlID0gOTAsIGZvbnQuc2l6ZSA9IDEyLCBoanVzdCA9IDEpICsgDQogICAgDQogICMgU2V0IHRoZSBuYW1lIGFuZCBvcmRlciBvZiBvdXIgQ2xhZGUgbGVnZW5kDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJOZXh0c3RyYWluXG5DbGFkZSIsIA0KICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyPTMpKQ0KDQojIHVzZSB0aGlzIGNvZGUgdG8gY3JlYXRlIGEgbmV3IGNvbG91ciBzY2FsZQ0KdHJlZS5wbG90IDwtIHRyZWUucGxvdCArIG5ld19zY2FsZV9maWxsKCkgDQoNCiMgR2VuZXJhdGUgYSBjYXRlZ29yaWNhbCBoZWF0bWFwIGxheWVyIGZvciB0aGUgUEFOR08gdmFyaWFibGUNCnRyZWUucGxvdCA8LSBnaGVhdG1hcCh0cmVlLnBsb3QsIGN1cmF0ZWRfdHJlZV9pbmZvLmRmICU+JSBzZWxlY3QoUEFOR08pLCANCiAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSAxLjAsIHdpZHRoID0gMC4xLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzX2FuZ2xlID0gOTAsIGZvbnQuc2l6ZSA9IDEyLCBoanVzdCA9IDEpICsgICAgIA0KDQogICMgU2V0IHRoZSBuYW1lIGFuZCBvcmRlciBvZiBvdXIgUEFOR08gbGVnZW5kDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbWJvLmNvbG91cnMsIG5hbWUgPSAiUEFOR09cbmxpbmVhZ2UiLCANCiAgICAgICAgICAgICAgICAgICAgZ3VpZGU9Z3VpZGVfbGVnZW5kKG9yZGVyPTQpKQ0KDQojIFZpZXcgdGhlIHRyZWUNCnRyZWUucGxvdA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS4xMS4wIEFubm90YXRlIGNvbm5lY3Rpb25zIGJldHdlZW4gdGlwcyB3aXRoIGBnZW9tX3RheGFsaW5rKClgDQoNClN1cHBvc2Ugd2Ugd2FudCB0byBqb2luIHRvIHRpcHMvdGF4YSB0b2dldGhlciB0byBzdWdnZXN0IHRoYXQgdGhlcmUgaXMgYSBub24tdHJlZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVtIG9yIHRvIGVtcGhhc2l6ZSBzb21lIGFzc29jaWF0aW9uIGJldHdlZW4gbm9kZXMuIFRvIGNvbm5lY3Qgbm9kZXMgb3IgdGlwcyBvZiB0aGUgdHJlZSwgd2UgY2FuIHVzZSB0aGUgYGdlb21fdGF4YWxpbmsoKWAgbGF5ZXIuDQoNCkxldCdzIGpvaW4gdGhlIG5vZGVzIGBDYW5hZGEvTkwtTk1MLTE3MTk0LzIwMjFgIGFuZCBgQ2FuYWRhL09OLVVIVEMtMDM2Ni8yMDIwYCB0b2dldGhlci4NCg0KYGBge3IsIGZpZy53aWR0aD0zMCwgZmlnLmhlaWdodD0zMH0NCg0KIyBTaG93IG91ciBjdXJhdGVkIHRyZWUgaW4gYSByZWN0YW5ndWxhciBmb3JtYXQNCg0KIyBXZSdsbCBuZWVkIHNvbWUgZXh0cmEgY29sb3VyIHNldHMgdG8gYWNjb21wbGlzaCB0aGlzIHBsb3QNCmNvbWJvLmNvbG91cnMgPSBjKGJyZXdlci5wYWwoMTIsICJQYWlyZWQiKSwgYnJld2VyLnBhbCg4LCAiU2V0MSIpLCBicmV3ZXIucGFsKDEyLCAiU2V0MyIpKQ0KDQojIFNhdmUgb3VyIGZpcnN0IHBsb3Qgd2l0aCBqdXN0IHRoZSB0cmVlDQp0cmVlLnBsb3QgPC0NCg0KICBTQzJfdmFyaWFudHNfdGltZS50cmVlICU+JQ0KICBkcm9wLnRpcCguLCBjdXJhdGVkX2Ryb3BfbGlzdCkgICU+JSANCg0KICAjIDEuIERhdGENCiAgZ2d0cmVlKC4sIGxheW91dCA9ICJyZWN0YW5ndWxhciIsIG1yc2QgPSAiMjAyMS0wMi0xNyIpICsgIyBOZWVkIHRoZSBtb3N0IHJlY2VudCBzYW1wbGluZyBkYXRlDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgDQogICAgIyBUaGVtZXMNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMCksDQogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICAgICAgKSArDQogICAgbGFicyh0aXRsZSA9ICJDYW5hZGEgYW5kIFVTIHNlcXVlbmNlZCBzdHJhaW4gcGh5bG9nZW55IiwNCiAgICAgICAgIGNvbG91ciA9ICJQcm92aW5jZSIpICsNCiAgICB2ZXhwYW5kKHJhdGlvID0gMC4xLCBkaXJlY3Rpb24gPSAtMSkgKyAjIEV4cGFuZCB0aGUgdmVydGljYWwgcGxvdCBzaXplDQoNCiAgICAjIFdlJ2xsIHdhbnQgdG8gZW5zdXJlIG91ciBjb2xvdXIgZ3VpZGUgZW5kcyB1cCBpbiB0aGUgY29ycmVjdCBvcmRlcg0KICAgIHNjYWxlX2NvbG91cl9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUHJvdmluY2UiLCBvcmRlcj0xKSkgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91ciA9IHN0cmFpbl9kaXZpc2lvbiksIHNpemUgPSA1KSArICMgQWRkIHRpcHMgb25seSANCiAgICBnZW9tX3RpcGxhYihzaXplID0gMTAsIGFsaWduPVRSVUUpICsgIyBBZGQgaW4gb3VyIGxhYmVscw0KDQogICAgIyMjIDEuMTEuMCBBZGQgaW4gc29tZSBhbm5vdGF0aW9uIGJldHdlZW4gcG9pbnRzDQogICAgZ2VvbV90YXhhbGluayh0YXhhMT0uLi4sIHRheGEyPS4uLiwgDQogICAgICAgICAgICAgICAgICBjb2xvcj0iYmx1ZSIsIGFscGhhPTAuOCwgb2Zmc2V0PTAsIA0KICAgICAgICAgICAgICAgICAgb3V0d2FyZD1GQUxTRSwNCiAgICAgICAgICAgICAgICAgIGFycm93PWFycm93KGxlbmd0aD11bml0KDAuMDEsICJucGMiKSkpDQoNCg0KIyBHZW5lcmF0ZSBvdXIgZmlyc3QgaGVhdG1hcCBsYXllcg0KdHJlZS5wbG90IDwtIGdoZWF0bWFwKHRyZWUucGxvdCwgY3VyYXRlZF90cmVlX2luZm8uZGYgJT4lIHNlbGVjdChwYXJlbnQsIG5vZGUsIGJyYW5jaC5sZW5ndGgpLCANCiAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSAwLjc1LCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDkwLCBmb250LnNpemUgPSA4LCBoanVzdCA9IDEpICsgDQogIA0KICAjIFNldCB0aGUgbmFtZSBvZiBvdXIgbGVnZW5kDQogIHNjYWxlX2ZpbGxfY29udGludW91cyhuYW1lID0gIlRyZWUgY29kaW5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpIA0KDQojIHVzZSB0aGlzIGNvZGUgdG8gY3JlYXRlIGEgbmV3IGNvbG91ciBzY2FsZQ0KdHJlZS5wbG90IDwtIHRyZWUucGxvdCArIG5ld19zY2FsZV9maWxsKCkgDQoNCiMgR2VuZXJhdGUgYSBjYXRlZ29yaWNhbCBoZWF0bWFwIGxheWVyIGZvciB0aGUgQ2xhZGUgdmFyaWFibGUNCnRyZWUucGxvdCA8LSBnaGVhdG1hcCh0cmVlLnBsb3QsIGN1cmF0ZWRfdHJlZV9pbmZvLmRmICU+JSBzZWxlY3QoQ2xhZGUpLCANCiAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSAwLjg1LCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDAsIGZvbnQuc2l6ZSA9IDEwKSArIA0KICAgIA0KICAjIFNldCB0aGUgbmFtZSBhbmQgb3JkZXIgb2Ygb3VyIENsYWRlIGxlZ2VuZA0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAiTmV4dHN0cmFpblxuQ2xhZGUiLCANCiAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlcj0zKSkNCg0KIyB1c2UgdGhpcyBjb2RlIHRvIGNyZWF0ZSBhIG5ldyBjb2xvdXIgc2NhbGUNCnRyZWUucGxvdCA8LSB0cmVlLnBsb3QgKyBuZXdfc2NhbGVfZmlsbCgpIA0KDQojIEdlbmVyYXRlIGEgY2F0ZWdvcmljYWwgaGVhdG1hcCBsYXllciBmb3IgdGhlIFBBTkdPIHZhcmlhYmxlDQp0cmVlLnBsb3QgPC0gZ2hlYXRtYXAodHJlZS5wbG90LCBjdXJhdGVkX3RyZWVfaW5mby5kZiAlPiUgc2VsZWN0KFBBTkdPKSwgDQogICAgICAgICAgICAgICAgICAgICAgb2Zmc2V0ID0gMS4wLCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDAsIGZvbnQuc2l6ZSA9IDEwKSArICAgICANCg0KICAjIFNldCB0aGUgbmFtZSBhbmQgb3JkZXIgb2Ygb3VyIFBBTkdPIGxlZ2VuZA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb21iby5jb2xvdXJzLCBuYW1lID0gIlBBTkdPXG5saW5lYWdlIiwgDQogICAgICAgICAgICAgICAgICAgIGd1aWRlPWd1aWRlX2xlZ2VuZChvcmRlcj00KSkNCg0KIyBWaWV3IHRoZSB0cmVlDQp0cmVlLnBsb3QNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMS4xMi4wIFRoZXJlIGlzIGRlZmluaXRlbHkgbW9yZSB0byBleHBsb3JlIGZyb20gdGhlIGBnZ3RyZWVgIHBhY2thZ2UNCg0KU28gd2UndmUgc3BlbnQgcXVpdGUgYSBiaXQgb2YgdGltZSBsb29raW5nIGF0IHBoeWxvZ2VuZXRpYyB0cmVlcyBhbmQgaG93IHRvIGFkZCBleHRlcm5hbCBkYXRhIHRvIHRpcHMgYW5kIG5vZGVzLiBXZSd2ZSBiYXJlbHkgc2NyYXRjaGVkIHRoZSBzdXJmYWNlIGFuZCB0aGVyZSBhcmUgYSBsb3Qgb2YgYWRkaXRpb25hbCBmdW5jdGlvbnMgd2l0aGluIHRoZSBgZ2d0cmVlYCBwYWNrYWdlIHRoYXQgeW91IGNhbiB3b3JrIHdpdGggdG8gYnVpbGQgYW1hemluZyBwbG90cyBpbmNsdWRpbmcgYWRkaW5nIGZhc3RhIHNlcXVlbmNlIGRhdGEgdGhyb3VnaCBgbXNhcGxvdCgpYC4gWW91IGNhbiBleHBlcmltZW50IHdpdGggbGFiZWxpbmcgaW50ZXJuYWwgbm9kZXMsIGFuZCBzcGVjaWZpYyBjbGFkZXMgYXMgd2VsbC4gWW91IGNhbiBhbHNvIGBmYWNldF9wbG90KClgIHJlY3Rhbmd1bGFyIHRyZWVzIHdpdGggb3RoZXIga2luZHMgb2YgcGxvdHMhDQoNCldlIGRpZG4ndCBldmVuIGhhdmUgdGltZSB0byBjb21iaW5lIGFsbCBzb3J0cyBvZiBwbG90IGRhdGEgd2l0aCBvdXIgY2lyY3VsYXIgdHJlZXMgdXNpbmcgdGhlIGBnZ3RyZWVFeHRyYWAgcGFja2FnZS4gRGVmaW5pdGVseSBjaGVjayBvdXQgW0NoYXB0ZXIgMTBdKGh0dHBzOi8veXVsYWItc211LnRvcC90cmVlZGF0YS1ib29rL2NoYXB0ZXIxMC5odG1sKSBvZiB0aGUgdHJlZSBib29rIHRvIGJlIGluc3BpcmVkIGJ5IHRoZSBwb3RlbnRpYWwgdGhpbmdzIHlvdSBjb3VsZCBkbyB3aXRoIG1vcmUgY29tcGxleCBkYXRhc2V0cyBsaWtlIHRoaXM6DQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovZ2d0cmVlX2dndHJlZUV4dHJhX21pY3JvYmlvbWUucG5nP3Jhdz10cnVlIiB3aWR0aD0iMTAwMCIvPg0KDQpUaGUgZ2VvbV9mcnVpdCgpIGxheWVyIGNhbiBiZSB1c2VkIHRvIGFkZCBleHRyYSB2aXN1YWxpemF0aW9uIHRvIHlvdXIgc3VuYnVyc3QgcGxvdCB0aHJvdWdoIHRoZSBnZ3RyZWVFeHRyYSBwYWNrYWdlDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMi4wLjAgV2hhdCBhcmUgbmV0d29yayBkaWFncmFtcyBhbmQgaG93IGNhbiB3ZSB1c2UgdGhlbT8NCg0KV2UndmUgc2VlbiBhIGxvdCBvZiB0cmVlcyB0b2RheSBidXQgYSBjbG9zZWx5IHJlbGF0ZWQgc3RydWN0dXJlIGlzIHRoZSBuZXR3b3JrIGRpYWdyYW0uIFRoZXkgc2hhcmUgc2ltaWxhciBjb25jZXB0cyBpbiB0aGF0IGJvdGggaGF2ZSBub2RlcyBhbmQgYnJhbmNoZXMuIEhvd2V2ZXIsIG5vZGVzIGFyZSBjYWxsZWQgKip2ZXJ0aWNlcyoqIGFuZCBjYW4gcmVwcmVzZW50IGFsbW9zdCBhbnl0aGluZywgYnJhbmNoZXMgYmV0d2VlbiBub2RlcyBhcmUgKiplZGdlcyoqIGFuZCBjYW4gc3BhbiBhY3Jvc3MgbXVsdGlwbGUgbm9kZXMsIGluc3RlYWQgb2YgaW4gYSBiaWZ1cmNhdGluZyB0cmVlIHJlbGF0aW9uc2hpcC4gUmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZlcnRpY2VzIGNhbiBiZSBiaWRpcmVjdGlvbmFsLCBhbmQgZGVwZW5kaW5nIG9uIHdoYXQgeW91J2QgbGlrZSB0byBwcmVzZW50LCBtdWx0aXBsZSBwYXJhbGxlbCBlZGdlcyBtYXkgZXhpc3QuIFdlIHNhdyBhIHNwZWNpZmljIHZlcnNpb24gb2YgbmV0d29yayBkaWFncmFtcyBpbiBMZWN0dXJlIDA0IHdpdGggb3VyIFNhbmtleSBwbG90cyENCg0KTmV0d29yayBncmFwaHMgYXJlIGdyZWF0IGZvciBzaG93aW5nIHRoZSBpbnRlcmNvbm5lY3Rpb25zIGJldHdlZW4gZW50aXRpZXMgd2l0aGluIHlvdXIgZGF0YS4gVGhpcyBraW5kIG9mIHZpc3VhbGl6YXRpb24gY2FuIGFsc28gaGVscCB1cyByZWFsaXplIHdoZXJlIHN1YnRsZSBjb25uZWN0aW9ucyBvY2N1ciAob3IgZG9uJ3Qgb2NjdXIhKS4gRGVwZW5kaW5nIG9uIGhvdyB5b3UndmUgd2VpZ2h0ZWQgb3IgY2hvc2VuIGVkZ2VzLCB5b3UgY2FuIGNvbnZleSBob3cgc3Ryb25nIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBlbnRpdGllcyBhcmUgYXMgd2VsbC4NCg0KVG8gd29yayB3aXRoIGdyYXBoIGRhdGEgYW5kIHBsb3QgaXQsIHdlJ2xsIGJlIHVzaW5nIGEgY291cGxlIG9mIGNvbXBhbmlvbiBwYWNrYWdlczogYHRpZHlncmFwaGAgYW5kIGBnZ3JhcGhgLiBNb3JlIGFib3V0IHRoYXQgbGF0ZXIhDQoNClNpbmNlIHdlIGFscmVhZHkgaGF2ZSBzb21lIG1ldGFkYXRhIGFib3V0IENPVklEIGdlbm9tZXMsIGxldCdzIHNlZSBpZiB3ZSBjYW4ndCBjb252ZXJ0IHNvbWUgb2YgaXQgdG8gYSBuZXR3b3JrIGRpYWdyYW0gdG8gYmV0dGVyIHVuZGVyc3RhbmQgc3RyYWluIGluZm9ybWF0aW9uPyBXZSdsbCBiZWdpbiBieSBzZWxlY3Rpbmcgc29tZSBpbmZvcm1hdGlvbiBmcm9tIG91ciBOZXh0c3RyYWluIG1ldGFkYXRhIHNldC4NCg0KYGBge3J9DQpzdHIoU0MyX21ldGFkYXRhLmRmLCBnaXZlLmF0dHIgPSBGQUxTRSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuMC4xIFBlcmZvcm0gYSBiaXQgb2YgZGF0YSB3cmFuZ2xpbmcgdG8gc2VsZWN0IG91ciBkYXRhDQoNCkJlZm9yZSB3ZSBqdW1wIGludG8gbWFraW5nIHNvbWUgbmV0d29yayBkaWFncmFtcywgd2UnbGwgd2FudCB0byB0cmltIGRvd24gdGhlIGRhdGEgZnJvbSBgU0MyX21ldGFkYXRhLmRmYC4gV2UgZG9uJ3QgbmVlZCBhbGwgb2YgdGhlIGRhdGEgdGhhdCBpcyBjdXJyZW50bHkgaW4gdGhpcyB0YWJsZS4gSW5zdGVhZCB3ZSdsbCB0cmltIGl0IGRvd24gdG8gMTAgdmFyaWFibGVzOg0KDQotICAgYFN0cmFpbmA6IHRoZSBzdHJhaW4gbmFtZSBvZiB0aGUgZ2Vub21lLiBUaGlzIG1hdGNoZXMgdGhlIHRpcHMgb2Ygb3VyIG9yaWdpbmFsIHRyZWVzIHdlIG1hZGUgaW4gc2VjdGlvbiAxLjAuMC4NCi0gICBgQ291bnRyeWA6IHRoZSBjb3VudHJ5IHdoZXJlIHRoZSBjYXNlIHdhcyByZXBvcnRlZC4NCi0gICBgQWRtaW4gRGl2aXNpb25gOiB0aGUgc3ViLXJlZ2lvbiB3aGVyZSB0aGUgY2FzZSB3YXMgaWRlbnRpZmllZA0KLSAgIGBBZG1pbiBEaXZpc2lvbiBvZiBleHBvc3VyZWA6IHRoZSBzdWItcmVnaW9uIGJlbGlldmVkIHRvIGJlIHRoZSBzb3VyY2Ugb2YgdGhlIHN0cmFpbi4NCi0gICBgUmVnaW9uYDogYXBwZWFycyB0byBiZSB0aGUgY29udGluZW50IHdoZXJlIHRoZSBjYXNlIHdhcyBpZGVudGlmaWVkLg0KLSAgIGBSZWdpb24gb2YgZXhwb3N1cmVgOiB0aGUgY29udGluZW50IHdhcyB0aGUgY2FzZSB3YXMgY29udHJhY3RlZC4NCi0gICBgTmV4dHN0cmFpbiBjbGFkZWA6IHRoZSBOZXh0c3RyYWluIGNsYWRlIGZvciB0aGlzIHN0cmFpbg0KLSAgIGBHSVNBSUQgY2xhZGVgOiB0aGUgY2xhZGUgY2xhc3NpZmljdGlvbiBkZWZpbmVkIGJ5IHRoZSBHbG9iYWwgSW5pdGlhdGl2ZSBvbiBTaGFyaW5nIEF2aWFuIEluZmx1ZW56YSBEYXRhDQotICAgYFBBTkdPIGxpbmVhZ2VgOiB0aGUgUGh5bG9nZW5ldGljIEFzc2lnbm1lbnQgb2YgTmFtZWQgR2xvYmFsIE91dGJyZWFrIGxpbmVhZ2UNCi0gICBgQ29sbGVjdGlvbiBEYXRhYDogdGhlIGNvbGxlY3Rpb24gZGF0ZSBvZiB0aGUgc3RyYWluIChhbHNvIG1pc3NwZWxsZWQpDQoNCmBgYHtyfQ0KU0MyX2dyYXBoX2luZm8uZGYgPC0NCg0KIyBQYXNzIGFsb25nIG91ciBtZXRhZGF0YQ0KU0MyX21ldGFkYXRhLmRmICU+JSANCg0KICAjIFdlJ2xsIGdyYWIgaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBjYXNlIHRoYXQgcmVsYXRlcyB0byBpdHMgZ2VvZ3JhcGhpY2FsIHJlZ2lvbg0KICAuLi4oU3RyYWluLCBDb3VudHJ5LCAnQWRtaW4gRGl2aXNpb24nLCAnQWRtaW4gRGl2aXNpb24gb2YgZXhwb3N1cmUnLCAnUmVnaW9uIG9mIGV4cG9zdXJlJywgUmVnaW9uLA0KICAgICAgICAgJ05leHRzdHJhaW4gY2xhZGUnLCAnR0lTQUlEIGNsYWRlJywgJ1BBTkdPIGxpbmVhZ2UnLCAnQ29sbGVjdGlvbiBEYXRhJykgJT4lIA0KICANCiAgIyBEbyBhIGJ1bmNoIG9mIHJlbmFtaW5nIGZvciBvdXIgdmFyaWFibGVzDQogIC4uLihzb3VyY2VfbG9jYXRpb24gPSAnQWRtaW4gRGl2aXNpb24gb2YgZXhwb3N1cmUnLCANCiAgICAgICAgIGNhc2VfbG9jYXRpb24gPSAnQWRtaW4gRGl2aXNpb24nLA0KICAgICAgICAgc291cmNlX3JlZ2lvbiA9ICdSZWdpb24gb2YgZXhwb3N1cmUnLA0KICAgICAgICAgY2FzZV9yZWdpb24gPSAnUmVnaW9uJywNCiAgICAgICAgIGNvbGxlY3Rpb25fZGF0ZSA9ICdDb2xsZWN0aW9uIERhdGEnLA0KICAgICAgICAgTmV4dHN0cmFpbiA9ICdOZXh0c3RyYWluIGNsYWRlJywNCiAgICAgICAgIEdJU0FJRCA9ICdHSVNBSUQgY2xhZGUnLA0KICAgICAgICAgUEFOR08gPSAnUEFOR08gbGluZWFnZScNCiAgICAgICAgKQ0KDQpoZWFkKFNDMl9ncmFwaF9pbmZvLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLjEuMCBVc2UgdGhlIGB0aWR5Z3JhcGhgIHBhY2thZ2UgdG8gaGVscCBwcmVwYXJlIGRhdGENCg0KTm93IHRoYXQgd2UgaGF2ZSBzb21lIGluZm9ybWF0aW9uIHRoYXQgd2Ugd2FudCB0byB3b3JrIHdpdGgsIHdlIHdhbnQgdG8gY29udmVydCB0aGF0IGtpbmQgb2YgZGF0YSB0byBzb21ldGhpbmcgdGhhdCBjYW4gYmUgaW50ZXJwcmV0ZWQgaW50byBhIGdyYXBoLiBUaGUgYHRpZHlncmFwaGAgcGFja2FnZSBwcm92aWRlcyBhIHdheSB0byBob29rIGdyYXBoIGRhdGEgaW50byB0aGUgYHRpZHl2ZXJzZWAgc28gdGhhdCB3ZSBjYW4gdXNlIGNvbW1vbiB2ZXJicyBhbmQgaWRlYXMgdG8gZmlsdGVyIGFuZCB3b3JrIHdpdGggaXQuIFVzaW5nIHRoaXMgcGFja2FnZSB3ZSBjYW4gY29udmVydCBvdXIgZGF0YWZyYW1lIGluZm9ybWF0aW9uIGludG8gYSBgdGJsX2dyYXBoYCBvYmplY3Qgd2hpY2ggaXMgYWN0dWFsbHkgYW4gYGlncmFwaGAgb2JqZWN0Lg0KDQpUaGUgZnVuY3Rpb24gd2UnbGwgdXNlIHRvIGNvbnZlcnQgb3VyIGRhdGEgaXMgYGFzX3RibF9ncmFwaCgpYCBidXQgaXQgaGFzIHNvbWUgZXhwZWN0YXRpb25zIGFib3V0IHRoZSBkYXRhLiBUaGUgcGFyYW1ldGVycyBvZiB0aGlzIGZ1bmN0aW9uIGFyZToNCg0KLSAgIGB4YDogdGhlIGRhdGEgZnJhbWUgd2UnZCBsaWtlIHRvIGNvbnZlcnQgdG8gYW4gYGlncmFwaGAuDQoNCi0gICBgbm9kZXNgOiBhIGRhdGEgZnJhbWUgd2l0aCBvdXIgbm9kZSBpbmZvcm1hdGlvbi4NCg0KLSAgIGBlZGdlc2A6IGEgZGF0YSBmcmFtZSBvZiB0d28gY29sdW1ucyBjb250YWluaW5nIGludGVnZXJzIG1hdGNoaW5nIG5vZGUgaW5mb3JtYXRpb24gdG8gZGVzY3JpYmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG5vZGVzLg0KDQpJZiB5b3UgaGF2ZSBib3RoIGEgbm9kZXMgYW5kIGVkZ2VzIGRhdGEgZnJhbWUgeW91IGNhbiBjZXJ0YWlubHkgZ2VuZXJhdGUgYSB0YWJsZSB0aGlzIHdheS4gSG93ZXZlciwgd2UgaGF2ZSBhIGNvbXBsZXggZGF0YWZyYW1lIGFuZCB3ZSB3YW50IHRvIHRyYWNrIGFsbCBvZiB0aGUgaW5mb3JtYXRpb24gaW4gaXQuIFRvIHRoYXQgZW5kIHdlIHdpbGwgKmFkZCogY29sdW1ucyBgZnJvbWAgYW5kIGB0b2AgYmFzZWQgb24gdmFyaWFibGVzIHRoYXQgYWxyZWFkeSBleGlzdCBhbmQgdGhlIGdyYXBoIG5vZGVzIHdpbGwgYmUgZ2VuZXJhdGVkIGZyb20gdGhpcyBpbmZvcm1hdGlvbi4gV2hlbiB3ZSBwcm92aWRlIHRoZSBkYXRhIGZyYW1lLCB0aGUgZnVuY3Rpb24gd2lsbCByZWNvZ25pemUgdGhlIGNvbHVtbnMgcHJlc2VudCBhbmQgcHJvZHVjZSBub2RlLWJhc2VkIGRhdGEgYW5kIGdlbmVyYXRlIGVkZ2UgY2hhcmFjdGVyaXN0aWNzIGZyb20gb3VyIG90aGVyIGNvbHVtbnMuDQoNCmBgYHtyfQ0KIyBOb3cgd2UnbGwgYWRkIHNvbWUgc3BlY2lmaWMgdmFyaWFibGVzIHVzZWQgdG8gZ2VuZXJhdGUgb3VyIGdyYXBoIHRhYmxlDQoNClNDMl9ncmFwaF9pbmZvLmRmICU+JSANCg0KICAjIENyZWF0ZSBvdXIgImZyb20iIGFuZCAidG8iIGNvbHVtbnMgaW4gb3VyIGRhdGEgZnJhbWUNCiAgbXV0YXRlKGZyb20gPSBzb3VyY2VfbG9jYXRpb24sDQogICAgICAgICB0byA9IENvdW50cnkNCiAgICAgICAgKSAlPiUgIA0KDQogICMgRmlsdGVyIGZvciBqdXN0IHN0cmFpbiBkYXRhIGZyb20gQ2FuYWRhIGFuZCBBc2lhDQogIGZpbHRlcihDb3VudHJ5ICVpbiUgYyguLi4pKSAlPiUgDQoNCiAgIyBDb252ZXJ0IHRvIGFuIGlncmFwaA0KICBhc190YmxfZ3JhcGgoKSAlPiUgDQoNCiAgIyBMb29rIGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGlncmFwaA0KICBzdHIoKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLjIuMCBQbG90IHlvdXIgZ3JhcGggaW5mb3JtYXRpb24gd2l0aCBgZ2dyYXBoKClgDQoNClRoZSBgZ2dyYXBoKClgIGZ1bmN0aW9uIGlzIG9uZSBvZiBtYW55IGZyb20gYSBwYWNrYWdlIG9mIHRoZSBzYW1lIG5hbWUuIFRoaXMgcGFja2FnZSBhZGRzIGdlb21zIGFuZCBhcmNoaXRlY3R1cmUgdGhhdCBpcyBjb21wYXRpYmxlIHdpdGggYGdncGxvdDJgIChmb3IgdGhlIG1vc3QgcGFydCkuIFNvbWUgb2YgdGhlIGZ1bmN0aW9ucyB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBhcmU6DQoNCnwgQ29tcG9uZW50IHwgRnVuY3Rpb24gICAgICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IEdyYXBoICAgICB8IGdncmFwaCAgICAgICAgICAgICB8IFRoZSBlcXVpdmFsZW50IG9mIHRoZSBjYWxsIHRvIGBnZ3Bsb3RgLCBpdCBzZXRzIHVwIHRoZSBiYXNpYyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZ3JhcGggcGxvdCAgICB8DQp8IEVkZ2UgICAgICB8IGdlb21fZWRnZV9saW5rICAgICB8IFByb2R1Y2VzIGVkZ2VzIGJldHdlZW4gcG9pbnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IEVkZ2UgICAgICB8IGdlb21fZWRnZV9mYW4gICAgICB8IFByb2R1Y2VzIGVkZ2VzIGJldHdlZW4gcG9pbnRzIGJ1dCBhY2NvdW50cyBmb3IgcGFyYWxsZWwgZWRnZXMsIGNyZWF0aW5nIGFyY3MgdGhhdCBmYW4gb3V0ICAgICAgICB8DQp8IEVkZ2UgICAgICB8IGdlb21fZWRnZV9wYXJhbGxlbCB8IFByb2R1Y2VzIG11bHRpcGxlIGVkZ2VzIGJldHdlZW4gcG9pbnRzIHdpdGggcGFyYWxsZWwgbGluZXMgcmVwcmVzZW50aW5nIHBhcmFsbGVsIGVkZ2VzICAgICAgICAgICB8DQp8IEVkZ2UgICAgICB8IGdlb21fZWRnZV9sb29wICAgICB8IFVzZWQgdG8gcmVwcmVzZW50IGVkZ2VzIHRoYXQgc3RhcnQgYW5kIGVuZCBhdCB0aGUgc2FtZSBub2RlLiBEb2VzIG5vdCBhY2NvdW50IGZvciBwYXJhbGxlbCBlZGdlcyB8DQp8IE5vZGUgICAgICB8IGdlb21fbm9kZV9wb2ludCAgICB8IEFkZCBiYXNpYyBub2RlcyB0byB5b3VyIGdyYXBoICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IE5vZGUgICAgICB8IGdlb21fbm9kZV9jaXJjbGUgICB8IEFkZCBub2RlcyB0byB5b3VyIGdyYXBoIHRoYXQgY2FuIGJlIHNjYWxlZCBieSB0aGUgY29vcmRpbmF0ZSBzeXN0ZW0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQoNClRoZSBgZ2dyYXBoKClgIGZ1bmN0aW9uIHRha2VzIGluIDMgcGFyYW1ldGVyczoNCg0KLSAgIGBncmFwaGA6IHRoZSBgaWdyYXBoYCBvYmplY3QgdGhhdCB3ZSdsbCBiYXNlIHRoZSBncmFwaCBvbi4NCg0KLSAgIGBsYXlvdXRgOiB0aGUgdHlwZSBvZiBsYXlvdXQgZm9yIHRoZSBncmFwaC4gVGhlcmUgYXJlIG1hbnkgb3B0aW9ucyBpbmNsdWRpbmc6IGF1dG8sIGRlbmRyb2dyYW0sIGxpbmVhciwgbWF0cml4LCB0cmVlbWFwICh5ZXAhKSBjaXJjbGVwYWNrLCBwYXJ0aXRpb24sIGFuZCBoaXZlLg0KDQotICAgYGNpcmN1bGFyYDogYSBsb2dpY2FsIHN0YXRpbmcgd2hldGhlciB0aGUgbGF5b3V0IHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBpbnRvIGEgcmFkaWFsIHJlcHJlc2VudGF0aW9uLiBJdCBjYW4ndCBiZSBhcHBsaWVkIHRvIGFsbCBsYXlvdXRzICh0aGluayBgcG9sYXJfY29vcmRgKS4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMn0NCg0KIyBOb3cgd2UnbGwgYWRkIHNvbWUgc3BlY2lmaWMgdmFyaWFibGVzIHVzZWQgdG8gZ2VuZXJhdGUgb3VyIGdyYXBoIHRhYmxlDQoNClNDMl9ncmFwaF9pbmZvLmRmICU+JSANCg0KICAjIEdlbmVyYXRlIG91ciBmcm9tIGFuZCB0byBlZGdlIGluZm9ybWF0aW9uDQogIG11dGF0ZShmcm9tID0gc291cmNlX3JlZ2lvbiwNCiAgICAgICAgdG8gPSBjYXNlX3JlZ2lvbg0KICAgICAgICApICU+JSAgDQoNCiAgIyBDb252ZXJ0IHRvIGFuIGlncmFwaCBhbmQgcGFzcyBpdCBvbiB0byBiZSBwbG90dGVkDQogIGFzX3RibF9ncmFwaCgpICU+JSANCg0KICAjIDEuIERhdGENCiAgZ2dyYXBoKC4pICsgDQogICAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKw0KDQogICAgICAjIyMgMi4yLjAgQWRkIG91ciBlZGdlcw0KICAgICAgLi4uKGFlcyhjb2xvdXIgPSBOZXh0c3RyYWluKSwgYXJyb3c9YXJyb3coKSwgd2lkdGggPSAxKSArDQoNCiAgICAgICMjIyAyLjIuMCBBZGQgb3VyIG5vZGVzIGFuZCBsYWJlbCB0aGVtDQogICAgICAuLi4oc2l6ZSA9IDcpICsgDQogICAgICAuLi4oYWVzKGxhYmVsID0gbmFtZSksIHNpemUgPSA1LCByZXBlbCA9IFRSVUUpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjIuMSBVc2UgYGdlb21fZWRnZV9mYW4oKWAgdG8gdmlzdWFsaXplIHBhcmFsbGVsIGVkZ2VzDQoNClNvIGZyb20gb3VyIGdyYXBoIHdlIGNhbiBzZWUgc29tZSBjaGFyYWN0ZXJpc3RpY3MgYWJvdXQgaG93IG91ciBjYXNlIGRhdGEgaXMgY29ubmVjdGVkLiBBbHRob3VnaCAqbW9zdCogb2YgdGhlIGRhdGEgaXMgY2VudHJlZCBhcm91bmQgZ2Vub21lcyBzZXF1ZW5jZWQgaW4gTm9ydGggQW1lcmljYSwgd2UgY2FuIHNlZSB0aGF0ICJOb3J0aCBBbWVyaWNhIiBpcyBhbHNvIGEgc291cmNlIHJlZ2lvbi4NCg0KVGhlIHdheSB3ZSBzZWUgdGhlIGVkZ2VzLCBob3dldmVyIGFsc28gaGFzIGFsbCBvZiB0aGVtIG92ZXJsYXlpbmcgb24gdG9wIG9mIGVhY2ggb3RoZXIuIFdlIGtub3cgdGhhdCB0aGlzIGlzbid0IGdvaW5nIHRvIGJlIGEgMToxIHJlbGF0aW9uc2hpcCBhbmQgYWx0aG91Z2ggd2UgaGF2ZSBjb2xvdXJlZCB0aGUgZWRnZXMsIHdlIG9ubHkgc2VlIHRoZSB0b3Btb3N0IGVkZ2UgaW4gYSBzdGFjayBvZiAqbWFueSouIEZvciBhIGdyYXBoIGxpa2UgdGhpcywgaWYgd2Ugd2FudCB0byBzZWUgYWxsIG9mIHRoZSBlZGdlcywgd2UgY2FuIHVzZSB0aGUgYGdlb21fZWRnZV9mYW4oKWAgbGF5ZXIgd2hpY2ggd2lsbCBoZWxwIHVzIHNlZSBhbGwgb2Ygb3VyIHBhcmFsbGVsIGVkZ2VzIGluIGFsbCBvZiB0aGVpciBzcGxlbmRvdXIuIE9mIG5vdGUsIHdlIGNhbiBhbHNvIHVzZSBgZ2VvbV9lZGdlX3BhcmFsbGVsKClgIGFzIGEgbGF5ZXIuIFdoaWxlIHRoaXMgbGF5ZXIgbWF5IG1ha2UgdGhlIG51bWJlciBvZiBjb25uZWN0aW9ucyBjbGVhcmVyLCBpdCBjYW4gZ2V0IHF1aXRlIGNyb3dkZWQuDQoNCkxldCdzIHRyeSBpdCBvdXQuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTJ9DQojIE5vdyB3ZSdsbCBhZGQgc29tZSBzcGVjaWZpYyB2YXJpYWJsZXMgdXNlZCB0byBnZW5lcmF0ZSBvdXIgZ3JhcGggdGFibGUNCg0KU0MyX2dyYXBoX2luZm8uZGYgJT4lIA0KDQogICMgR2VuZXJhdGUgb3VyIGZyb20gYW5kIHRvIGVkZ2UgaW5mb3JtYXRpb24NCiAgbXV0YXRlKGZyb20gPSBzb3VyY2VfcmVnaW9uLA0KICAgICAgICB0byA9IGNhc2VfcmVnaW9uDQogICAgICAgICkgJT4lICANCg0KICAjIENvbnZlcnQgdG8gYW4gaWdyYXBoDQogIGFzX3RibF9ncmFwaCgpICU+JSANCg0KICAjIDEuIERhdGENCg0KICBnZ3JhcGgoLikgKyANCiAgICAgICMgMi4gQWVzdGhldGljcw0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArDQoNCiAgICAgICMjIyAyLjIuMSBBZGQgb3VyIGVkZ2VzDQogICAgICAuLi4oYWVzKGNvbG91ciA9IC4uLiksIGFycm93PWFycm93KCkpICsNCg0KICAgICAgIyBBZGQgb3VyIG5vZGVzIGFuZCBsYWJlbCB0aGVtDQogICAgICBnZW9tX25vZGVfcG9pbnQoc2l6ZSA9IDcpICsgDQogICAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgc2l6ZSA9IDUsIHJlcGVsID0gVFJVRSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuMi4yIEZpbHRlciBhbmQgY2hvb3NlIHlvdXIgZGF0YSBjYXJlZnVsbHkgdG8gYmV0dGVyIHZpc3VhbGl6ZSBpdA0KDQpUaGUgbWV0YWRhdGEgd2UndmUgY2hvc2VuIGlzbid0IHZlcnkgZGV0YWlsZWQgYW5kIHRoYXQgY2FuIGJlIHRoZSBjYXNlIGZvciBtYW55IGRhdGFzZXRzIHNvIHlvdSdsbCB3YW50IHRvIGJlIHN1cmUgb2YgaG93IHlvdSBwaWNrIHlvdXIgbm9kZXMuIFJpZ2h0IG5vdyB3ZSBhcmUgcGlja2luZyBvdXIgYGZyb21gIGFuZCBgdG9gIHZhbHVlcyBiYXNlZCBvbiB0aGUgc3VwcG9zZWQgc291cmNlIGFuZCBpZGVudGlmeWluZyBjb250aW5lbnRzIG9mIHRoZSBzdHJhaW4uDQoNCldoaWxlIG90aGVyIHZhcmlhYmxlcyBjb3VsZCBvZmZlciBtb3JlIGluZm9ybWF0aW9uLCB0aGV5IGNhbiB2YXJ5IGluIGNvbnNpc3RlbmN5IGZyb20gYSByZWdpb24gKEFzaWEpIHRvIGEgcHJvdmluY2UgKE9udGFyaW8pIGFzIHNlZW4gaW4gdmFyaWFibGUgYGNhc2VfbG9jYXRpb25gLiBEZWZpbml0ZWx5IGFpbSB0byBiZSBjb25zaXN0ZW50IHdoZW4geW91J3JlIHdvcmtpbmcgd2l0aCB5b3VyIGRhdGEgb3RoZXJ3aXNlIHRoZSBub2RlcyBtYXkgaGF2ZSBsZXNzIG1lYW5pbmcuDQoNCkxldCdzIHRyeSBsb29raW5nIGF0IGBzb3VyY2VfcmVnaW9uYCAoQ29udGluZW50KSB0byBgY2FzZV9sb2NhdGlvbmAgKERpdmlzaW9ucykgYXMgaXQgcmVsYXRlcyB0byBkYXRhIGZyb20gQ2FuYWRhIGFuZCBBc2lhDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTJ9DQoNCiMgTm93IHdlJ2xsIGFkZCBzb21lIHNwZWNpZmljIHZhcmlhYmxlcyB1c2VkIHRvIGdlbmVyYXRlIG91ciBncmFwaCB0YWJsZQ0KDQpTQzJfZ3JhcGhfaW5mby5kZiAlPiUgDQoNCiAgIyBHZW5lcmF0ZSBvdXIgZnJvbSBhbmQgdG8gZWRnZSBpbmZvcm1hdGlvbg0KICBtdXRhdGUoZnJvbSA9IC4uLiwNCiAgICAgICAgdG8gPSAuLi4NCiAgICAgICAgKSAlPiUgIA0KDQogICMgRmlsdGVyIGZvciBDYW5hZGEgZGF0YQ0KICBmaWx0ZXIoQ291bnRyeSAlaW4lIGMoIkNhbmFkYSIsICJBc2lhIikpICU+JSANCg0KICAjIENvbnZlcnQgdG8gYW4gaWdyYXBoDQogIGFzX3RibF9ncmFwaCgpICU+JSANCg0KICAjIDEuIERhdGENCiAgZ2dyYXBoKC4pICsgDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArDQoNCiAgICAjIEFkZCBvdXIgZWRnZXMNCiAgICBnZW9tX2VkZ2VfZmFuKGFlcyhjb2xvdXIgPSBOZXh0c3RyYWluKSwgYXJyb3c9YXJyb3coKSkgKw0KICAgICMjIyAyLjIuMiBJZiB3ZSBzdXNwZWN0IGxvb3BpbmcgZWRnZXMsIHdlIG5lZWQgdG8gZGVmaW5lIHRoYXQgbGF5ZXIgc3BlY2lmaWNhbGx5DQogICAgLi4uICsNCg0KICAgICMgQWRkIG91ciBub2RlcyBhbmQgbGFiZWwgdGhlbQ0KICAgIGdlb21fbm9kZV9wb2ludChzaXplID0gNykgKyANCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgc2l6ZSA9IDcsIHJlcGVsID0gVFJVRSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMi4zLjAgVGhlcmUgYXJlIG1hbnkgbW9yZSB2aXN1YWxpemF0aW9ucyBmcm9tIGBnZ3JhcGhgDQoNCkludGVyZXN0aW5nbHkgaW4gdGhlIGZpcnN0IHllYXIgb2YgdGhlIHBhbmRlbWljLCBmcm9tIHRoZSBzZXF1ZW5jZWQgZ2Vub21lIGRhdGEsIGluZmVjdGlvbnMgd2l0aGluIENhbmFkYSBhcHBlYXIgdG8gb3JpZ2luYXRlIG1vc3RseSBmcm9tIHdpdGhpbiBOb3J0aCBBbWVyaWNhIGJ1dCB0aGVyZSBhcmUgc29tZSBpbmZlY3Rpb25zIHRoYXQgb3JpZ2luYXRlZCBmcm9tIEFzaWEsIGFuZCBlbnRlcmVkIHRocm91Z2ggT250YXJpbyBhbmQgQnJpdGlzaCBDb2x1bWJpYSAtIG91ciB0d28gbWFpbiBnbG9iYWwgcG9ydHMgb2YgZW50cnkhDQoNCldlJ3ZlIHJlYWxseSBvbmx5IGNvdmVyZWQgbGluZWFyIGdyYXBoIGxheW91dHMgaW4gb3VyIGRhdGEgYnV0IHRoZXJlIGFyZSBhY3R1YWxseSBtYW55ICpraW5kcyogb2YgZ3JhcGhzIHRoYXQgdGhpcyBwYWNrYWdlIGNhbiBwcm9kdWNlLiBUaGUgYGxheW91dGAgcGFyYW1ldGVyIGlzIHRoZSBrZXkgdG8gZXhwbG9yaW5nIGFsbCBvZiB0aGUgZ3JhcGggdHlwZXMgYXZhaWxhYmxlLiBPZiBjb3Vyc2UgeW91ciBkYXRhIGxheW91dCBhbGwgaGFzIHRvIG1ha2Ugc2Vuc2UhIFlvdSdsbCBmaW5kIGdyZWF0IGV4YW1wbGVzIGZyb20gW2RhdGEtaW1hZ2luaXN0LmNvbV0oaHR0cHM6Ly93d3cuZGF0YS1pbWFnaW5pc3QuY29tLzIwMTcvZ2dyYXBoLWludHJvZHVjdGlvbi1sYXlvdXRzLykgbGlrZSB0aGlzIGNpcmNsZXBhY2sgZ3JhcGg6DQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovY2lyY2xlcGFjay5wbmc/cmF3PXRydWUiIHdpZHRoPSI3MDAiLz4NCg0KVGhlcmUgYXJlIGFsbCBraW5kcyBvZiBuZXR3b3JrIGdyYXBocyB0aGF0IGNhbiB1c2UgYWRkaXRpb25hbCBsYXllcnMgb2YgbWV0YWRhdGEgdG8gc2hhcGUgYW5kIHByZXNlbnQgeW91ciBkYXRhDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMy4wLjAgU2VxdWVuY2UgbW90aWZzIGFuZCBnZW5vbWljIG1hcmtlcnMNCg0KV2hlbiB3b3JraW5nIHdpdGggZGF0YSBzb21ldGltZXMgeW91IG1heSBiZSB3b3JraW5nIHdpdGggc2VxdWVuY2Utc3BlY2lmaWMgZGF0YSBmb3IgYW5hbHlzaXMgb2YgbW90aWZzIG9yIHlvdSBtYXkgaGF2ZSBhIGxhcmdlIHNldCBvZiBkYXRhIGRlc2NyaWJpbmcgZ2Vub21pYyBtYXJrZXJzIGxpa2Ugc2luZ2xlIG51Y2xlb3RpZGUgdmFyaWFudHMuIFR3byBwb3B1bGFyIHZpc3VhbGl6YXRpb25zIGluIHRoaXMgZG9tYWluIGFyZSB0aGUgc2VxdWVuY2UgbG9nbyBhbmQgTWFuaGF0dGFuIHBsb3QuDQoNCiMjIDMuMS4wIEdlbmVyYXRlIHNlcXVlbmNlIGxvZ29zIHdpdGggYGdnc2VxbG9nb2ANCg0KVGhpcyBvbmUgaXMgc2hvcnQgYW5kIHNpbXBsZS4gSWYgeW91IGhhdmUgYSBzZXJpZXMgb2Ygc2VxdWVuY2VzIHlvdSdkIGxpa2UgdG8gcGxvdCBiYXNlZCBvbiBmcmVxdWVuY3kgb2YgZWFjaCBiYXNlcyB5b3Ugc2VlIGF0IGVhY2ggcG9zaXRpb24sIHRoZSBzZXF1ZW5jZSBsb2dvIGlzIGZvciB5b3UuIFRoZSBgZ2dzZXFsb2dvYCBwYWNrYWdlIG9mZmVycyBhIHNpbXBsZSBgZ2dwbG90MmAtY29tcGF0aWJsZSBzZXQgb2YgZ2VvbXMgeW91IGNhbiBhZGQgdG8geW91ciBncmFwaCBpbiBvcmRlciB0byByZXByZXNlbnQgdGhpcyBkYXRhLg0KDQpZb3VyIGRhdGEgY2FuIGNvbWUgaW4gdHdvIGZsYXZvdXJzOg0KDQoxLiAgQSBuYW1lZCBsaXN0IHdoZXJlIGVhY2ggaW5kZXggcG9zaXRpb24gaXMgYSB2ZWN0b3Igb2Ygc2VxdWVuY2VzLCBhbmQgdGhlIG5hbWVzIG9mIGVhY2ggaW5kZXggY2FuIGhhdmUgbWVhbmluZyAobGlrZSBhIHRyYW5zY3JpcHRpb24gZmFjdG9yKS4NCg0KMi4gIEEgZnJlcXVlbmN5IG1hdHJpeCB3aGVyZSBjb2x1bW5zIGNvcnJlc3BvbmQgdG8gcG9zaXRpb25zLCBhbmQgcm93bmFtZXMgY29ycmVzcG9uZCB0byBiYXNlcyAob3IgYW1pbm8gYWNpZHMpLiBFYWNoIGVudHJ5IGlzIHRoZSBudW1iZXIgb2YgdGltZXMgdGhhdCBhIGJhc2UgaXMgc2VlbiBhdCBhIHNwZWNpZmljIHBvc2l0aW9uLiBUaGVzZSBtYXRyaWNlcyBjb3VsZCBiZSBkYWlzeS1jaGFpbmVkIGluIGEgbGlzdC4NCg0KTGV0J3Mgd29yayB3aXRoIHRoZSBmb3JtZXIgc2luY2UgaXQgZG9lcyBoYXZlIHNvbWUgZmxleGliaWxpdHkgYW5kIGxvb2tzIGxpa2Ugc29tZXRoaW5nIHdlIHJlY29nbml6ZS4NCg0KYGBge3J9DQojIEJ1aWxkIGFuIGV4YW1wbGUgc2VxdWVuY2Ugc2V0IChpdCdzIGFjdHVhbGx5IGZyb20gdGhlIFNBUlMtQ29WLTIgc3Bpa2Ugc2VxdWVuY2UgKQ0KZXhhbXBsZV9zZXExIDwtIGMoImFhY2NjYWN0YWF0Z2d0Z3R0Z2d0dCIsImFhdGNjYWN0YWF0Z2d0Z3R0Z2N0dCIsImFhY2NjYWF0YWF0YWd0Z3R0Z2d0dCIsDQogICAgICAgICAgICAgICAgICJhYWNjY2FjdGFhdGdndGd0dGdndHQiLCJhYWNjY2FjdGFhdGdndGF0dGdhdHQiLCJhY2NjY2FjdGFhdGd0dGd0dGdndHQiLA0KICAgICAgICAgICAgICAgICAiYWFjY2NhY3RhYXRnZ3RndHRnZ3R0IiwiYWFjY2NhY3RhYXRnZ3RhdHRnYXR0IiwiYWNjY2NhY3RhYXRndHRndHRnZ3R0IikgJT4lIA0KDQogICMgQ29udmVydCBpdCBhbGwgdG8gdXBwZXIgY2FzZSBiZWNhdXNlIGdnc2VxbG9nbyBhcHBlYXJzIHRvIGhhdGUgd2hlbiB3ZSBkb24ndA0KICBzdHJfdG9fdXBwZXIoKQ0KDQpleGFtcGxlX3NlcTIgPC0gYygiY2FjY2NhY3RhYXRnZ3RndHRnZ3RnIiwiYWF0Y2NhY3RhYXRnZ3RndHRnY3R0IiwiYWFjY2NhYXRhYXRhZ3RndHRnZ3R0IiwNCiAgICAgICAgICAgICAgICAgImNhY2NjYWN0YWF0Z2d0Z3R0Z2d0ZyIsImFhY2NjYWN0YWF0Z2d0YXR0Z2F0dCIsImFjY2NjYWN0YWF0Z3R0Z3R0Z2d0dCIsDQogICAgICAgICAgICAgICAgICJjYWNjY2FjdGFhdGdndGd0dGdndGciLCJhYWNjY2FjdGFhdGdndGF0dGdhdHQiLCJhY2NjY2FjdGFhdGd0dGd0dGdndHQiKSAlPiUgDQoNCiAgIyBDb252ZXJ0IGl0IGFsbCB0byB1cHBlciBjYXNlIGJlY2F1c2UgZ2dzZXFsb2dvIGFwcGVhcnMgdG8gaGF0ZSB3aGVuIHdlIGRvbid0DQogIHN0cl90b191cHBlcigpDQoNCiMgQnVpbGQgb3VyIGxpc3QNCmV4YW1wbGVfc2VxLmxpc3QgPSBsaXN0KHNwaWtlX3NldDEgPSAuLi4sIHNwaWtlX3NldDIgPSAuLi4pDQoNCiMgVGFrZSBhIGxvb2sgYXQgb3VyIGxpc3QNCnN0cihleGFtcGxlX3NlcS5saXN0KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4xLjEgUGxvdCB5b3VyIGxvZ28gaW5mb3JtYXRpb24gd2l0aCB0aGUgYGdnc2VxbG9nbygpYCB3cmFwcGVyIGZ1bmN0aW9uDQoNCldlIGhhdmUgdHdvIG9wdGlvbnMgdG8gbG9vayBhdCBvdXIgc2VxdWVuY2UgZGF0YS4gVGhlIGBnZ3NlcWxvZ28oKWAgaXMgYSB3cmFwcGVyIHRoYXQgc2V0cyB1cCB0aGUgZW50aXJlIHBsb3QgZm9yIHVzIGluY2x1ZGluZyBpZiB5b3UnZCBsaWtlIHRvIGZhY2V0IHRoZSBkYXRhLiBJZiB5b3UganVzdCB3YW50IGEgc2luZ2xlIGxvZ28gdGhlbiB5b3UgcGFzcyBwYXJ0cyBvZiB0aGUgbGlzdCBpbmRpdmlkdWFsbHkuDQoNCklmIHlvdSdkIGxpa2UgbXVsdGlwbGUgbG9nb3MsIHlvdSBjYW4gZGVjaWRlZCB0aGVpciBsYXlvdXQgd2l0aCB0aGUgYG5jb2xgIG9yIGBucm93YCBwYXJhbWV0ZXJzLiBUaGUgb3RoZXIgcGFyYW1ldGVycyBhcmU6DQoNCi0gICBgZmFjZXRgIHdoaWNoIHdpbGwgYWNjZXB0IGVpdGhlciAid3JhcCIgb3IgImdyaWQiLg0KDQotICAgYHNjYWxlc2Agd2hpY2ggd2lsbCBhY2NlcHQgdGhlIHNjYWxlIHR5cGVzIGZvciB0aGUgZmFjZXQgZ2VvbS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD00fQ0KDQojIE1ha2UgYSAyLXJvdyBzZXF1ZW5jZSBsb2dvDQpnZ3NlcWxvZ28oLi4uLCAuLi4pICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArDQogIGxhYnModGl0bGUgPSAiRmFjZXQgaW4gcm93cyIpDQoNCiMgTWFrZSBhIDItY29sdW1uIHNlcXVlbmNlIGxvZ28NCmdnc2VxbG9nbyguLi4sIC4uLikgKw0KICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpICsNCiAgbGFicyh0aXRsZSA9ICJGYWNldCBpbiBjb2x1bW5zIikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMS4yIFBsb3QgeW91ciBsb2dvIGluZm9ybWF0aW9uIHdpdGggYGdlb21fbG9nbygpYA0KDQpUbyBwbG90IG91ciBkYXRhIHdlIGNhbiB1c2UgYGdncGxvdGAgbGFuZ3VhZ2UgdGhhdCB3ZSBhcmUgZmFtaWxpYXIgd2l0aCBhbmQgYWRkIGEgbGF5ZXIgdG8gb3VyIHBsb3Qgd2l0aCB0aGUgYGdlb21fbG9nbygpYCBmdW5jdGlvbi4gVGhpcyBsYXllciBpcyBhbHNvIHN1cHBsaWVkIGJ5IHRoZSBgZ2dzZXFsb2dvYCBwYWNrYWdlLiBJbiBvcmRlciBmb3IgdGhpcyB0byBmdW5jdGlvbiBwcm9wZXJseSwgaG93ZXZlciwgd2UgbmVlZCB0byBkZWZpbmUgb3VyIHBhcmFtZXRlcnMgYXM6DQoNCi0gICBgZGF0YWA6IHRoZSB2ZWN0b3Igd2l0aCBvZiBvdXIgc2VxdWVuY2UgZGF0YQ0KDQotICAgYHNlcV90eXBlYDogc2V0cyB0aGUgdHlwZSBvZiBzZXF1ZW5jZSBsaWtlICJETkEiLCAiUk5BIiBvciAiQUEiDQoNCi0gICBgbWV0aG9kYDogdGhlIHktc2NhbGUgb2Ygb3VyIGxvZ28gZWl0aGVyIGluICJiaXRzIiAoaW5mb3JtYXRpb24gdmFsdWUpIG9yIGJ5ICJwcm9iYWJpbGl0eSINCg0KVGhlc2UgYW5kIG90aGVyIHBhcmFtZXRlcnMgY2FuIGFsc28gYmUgc2V0IGluIHRoZSBgZ2dzZXFsb2dvKClgIGZ1bmN0aW9uIHRvby4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD00fQ0KDQojIDEuIERhdGENCmdncGxvdCgpICsgDQogICMgMi4gQWVzdGhldGljcw0KICB0aGVtZV9sb2dvKCkgKyANCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArDQogIGxhYnModGl0bGUgPSAiRmFjZXQgaW4gY29sdW1ucyB3aXRoIGdlb21fbG9nbygpIikgKw0KDQogICMgNC4gR2VvbXMNCiAgZ2VvbV9sb2dvKGRhdGEgPSBleGFtcGxlX3NlcS5saXN0LCBzZXFfdHlwZT0iRE5BIiwgbWV0aG9kID0gImJpdHMiKSArIA0KICANCiAgIyA2LiBGYWNldA0KICBmYWNldF93cmFwKC4uLikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMy4yLjAgVGhlIE1hbmhhdHRhbiBwbG90IGZvciBhbGwgeW91ciBnZW5vbWUtcG9zaXRpb24gdmlzdWFsaXphdGlvbiBuZWVkcw0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L01hbmhhdHRhbl9leGFtcGxlLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KDQpUaGUgTWFuaGF0dGFuIHBsb3QgaXMgdXN1YWxseSB1c2VkIGluIHZpc3VhbGl6aW5nIGRhdGEgYWNyb3NzIHRoZSBnZW5vbWUgZnJvbSBMT0Qgc2NvcmVzIHRvIG1hcmtlciBwcm9wb3J0aW9ucy4NCjo6Og0KDQpOYW1lZCBmb3IgaXQncyB2aXN1YWwgc2ltaWxhcml0eSB0byB0aGUgTWFuaGF0dGFuIHNreWxpbmUgb2Ygc2luZ3VsYXIgdG93ZXJpbmcgc2t5c2NyYXBlcnMgc2NhdHRlcmVkIGFib3ZlIGxvdy1sZXZlbCBidWlsZGluZ3MsIHRoaXMgcGxvdCBpcyBjb21tb25seSB1c2VkIGZvciB2aXN1YWxpemluZyBnZW5vbWljIG1hcmtlcnMgYWNyb3NzIGFuIG9yZGVyZWQgbGluZWFyIGF4aXMgbGlrZSBhIHNlcmllcyBvZiBjaHJvbW9zb21lcy4gVGhlIHktYXhpcyBvZiB0aGUgdmFsdWVzIGNhbiByZXByZXNlbnQgTE9EIHNjb3JlcyBvciBwcm9wb3J0aW9ucyBvZiBtYXJrZXIgcmVwcmVzZW50YXRpb24uIFRoaXMgaXMgZnJlcXVlbnRseSB1c2VkIHRvIHZpc3VhbGl6ZSBHV0FTIG9yIG1hcHBpbmcgZGF0YS4NCg0KVGhpcyBpcyBhbm90aGVyIHNjYXR0ZXJwbG90IHRoYXQgd2UgY2FuIGdlbmVyYXRlIGRpcmVjdGx5IGluIGBnZ3Bsb3RgIHdpdGggc29tZSBlZmZvcnQvc2V0dXAgb3Igd2UgY2FuIHVzZSBhIHBhY2thZ2UgbGlrZSBgcXFtYW5gLg0KDQpGaXJzdCBsZXQncyBsb29rIGF0IHRoZSBraW5kIG9mIGRhdGEuIEZ1biBmYWN0ISBZb3UgY2FuIHJlYWQgaW4gYXJjaGl2ZWQvemlwcGVkIGRhdGEgYXMgd2VsbCwgYWx0aG91Z2ggYmUgY2FyZWZ1bCwgR1dBUyBmaWxlcyBjYW4gYmUgcXVpdGUgbGFyZ2UhDQoNCmBgYHtyfQ0KbG9hZCguLi4pDQoNCnN0ciguLi4sIGdpdmUuYXR0ciA9IEZBTFNFKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4yLjEgVXNlIHRoZSBgbWFuaGF0dGFuKClgIGZ1bmN0aW9uIHRvIGdlbmVyYXRlIGEgcGxvdA0KDQpUbyBnZW5lcmF0ZSBvdXIgTWFuaGF0dGFuIHBsb3Qgd2UgY2FuIHVzZSB0aGUgYG1hbmhhdHRhbigpYCB3cmFwcGVyIGZ1bmN0aW9uIHdoaWNoIHdpbGwgcmVxdWlyZSBmb3VyIHNldHMgb2YgcGFyYW1ldGVyIGluZm9ybWF0aW9uOg0KDQotICAgYGNocmA6IHRoZSBjb2x1bW4gbmFtZSB3aXRoIHRoZSBjaHJvbW9zb21lIGluZm9ybWF0aW9uIGZvciBhIGdpdmVuIFNOUC4NCg0KLSAgIGBicGA6IHRoZSBiYXNlcGFpciBwb3NpdGlvbiBvZiB0aGUgU05QLg0KDQotICAgYHNucGA6IHRoZSBJRCBvZiB0aGUgU05QIHVzdWFsbHkgYSBSZWZTTlAgSUQgKFJTSUQpIG9mIHNvbWUga2luZC4NCg0KLSAgIGBwYDogdGhlIHAtdmFsdWUgZm9yIHRoYXQgcGFydGljdWxhciBTTlAgZ2VuZXJhdGVkIGZyb20gdGhlIGNhc2UgdnMuIGNvbnRyb2wgYW5hbHlzaXMuIFRoaXMgd2lsbCBiZSBjb252ZXJ0ZWQgdG8gYSAkLWxvZzEwKHApJCB2YWx1ZSBmb3IgdGhlIHktYXhpcy4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBtYW5oYXR0YW4oZ3dhc1Jlc3VsdHMsIGNocj0iQ0hSIiwgYnA9IkJQIiwgc25wPSJTTlAiLCBwPSJQIikNCg0KR1dBU19kYXRhLmRmICU+JSANCiMgWW91IGNhbiBmaWx0ZXIgeW91ciBkYXRhIGlmIHlvdSB3YW50IHRoaXMgc3RlcCB0byBydW4gZmFzdGVyDQojIGZpbHRlcihDSFIgJWluJSBjKDE6NSkpICU+JSAgDQoNCiAgLi4uKC4sIGNocj0iQ0hSIiwgYnA9IlBPUyIsIHNucD0icnNpZCIsIHA9InAudmFsdWUiKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4yLjIgVXNlIHRoZSBgaGlnaGxpZ2h0YCBwYXJhbWV0ZXIgdG8gaWRlbnRpZnkgbWFya2VycyBvZiBpbnRlcmVzdA0KDQpOb3cgdGhhdCB3ZSd2ZSBwbG90dGVkIG91ciBNYW5oYXR0YW4gcGxvdCwgd2UgY2FuIHNlZSBzb21lIGhvcml6b250YWwgbGluZXMgY29ycmVzcG9uZGluZyB0byBtaW5pbXVtIHAtdmFsdWVzIG9mIDEuMCB4IDEwXi01XiBhbmQgNS4wIHggMTBeLTheIGFsdGhvdWdoIHRoZSBzaWduaWZpY2FuY2Ugb2YgdGhlc2UgY2FuIGRlcGVuZCBvbiBzYW1wbGUgc2l6ZSBhbmQgYWxsZWxlIGZyZXF1ZW5jaWVzIG9mIHlvdXIgU05Qcy4NCg0KRWl0aGVyIHdheSwgeW91IGNhbiBoaWdobGlnaHQgU05QIHJlc3VsdHMgYmFzZWQgb24gYSBwcm92aWRlZCBsaXN0LiBJbiB0aGlzIGNhc2UsIHdlIGNhbiBnZW5lcmF0ZSBhIGxpc3Qgb3Vyc2VsdmVzIGJ5IGZpbHRlcmluZyBvbiB0aGUgYHAudmFsdWVgIHZhcmlhYmxlLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQpzbnBfY2FuZGlkYXRlcyA8LSANCg0KICAjIFBhc3MgYWxvbmcgb3VyIEdXQVMgZGF0YQ0KICBHV0FTX2RhdGEuZGYgJT4lIA0KDQogICMgRmlsdGVyIGl0IGZvciBsb3cgcC12YWx1ZXMNCiAgZmlsdGVyKC1sb2cxMChwLnZhbHVlKSA+PSA1KSAlPiUgDQoNCiAgIyBTZWxlY3QgU05QIGNhbmRpZGF0ZXMNCiAgcHVsbCguLi4pICU+JSANCiAgdW5pcXVlKCkNCg0KIyBzbnBfY2FuZGlkYXRlcw0KDQojIFBsb3Qgb3VyIE1hbmhhdHRhbiBwbG90DQptYW5oYXR0YW4oR1dBU19kYXRhLmRmLCANCiAgICAgICAgICBjaHI9IkNIUiIsIA0KICAgICAgICAgIGJwPSJQT1MiLCANCiAgICAgICAgICBzbnA9InJzaWQiLCANCiAgICAgICAgICBwPSJwLnZhbHVlIiwgDQogICAgICAgICAgaGlnaGxpZ2h0ID0gLi4uKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDQuMC4wIFRpcHMgYW5kIFRyaWNrcw0KDQpXaGVuIHByZXBhcmluZyBmaWd1cmVzIGZvciBhIHByZXNlbnRhdGlvbiBvciBtYW51c2NyaXB0IGNvbnNpZGVyIHNvbWUgb2YgdGhlIGZvbGxvd2luZyB0aXBzL3RyaWNrcyB0aGF0IEkndmUgYWNjdW11bGF0ZWQgb3ZlciB0aGUgeWVhcnMuIE5vdCBhbGwgbWF5IGFwcGx5IHRvIHlvdXIgd29yayBidXQgdGhlc2UgY2FuIGNlcnRhaW5seSBiZSBoZWxwZnVsLg0KDQojIyA0LjEuMCBIb3cgbWFueSBmaWd1cmVzIGRvIHlvdSBuZWVkIHRvIHRlbGwgeW91ciBzdG9yeT8NCg0KUmVnYXJkbGVzcyBvZiB3aGV0aGVyIG9yIG5vdCB5b3UnbGwgYmUgZ2l2aW5nIGEgdGFsayBvciBzdWJtaXR0aW5nIGEgbWFudXNjcmlwdCB5b3UnbGwgd2FudCB0byBjb25zaWRlciBob3cgbWFueSBmaWd1cmVzIHlvdSBuZWVkIHRvIHRlbGwgeW91ciBzdG9yeS4NCg0KLSAgICoqTW9zdCBtYWpvciBwdWJsaWNhdGlvbnMqKiB3aWxsIGhhdmUgNC02IG1haW4gZmlndXJlcyB3aXRoIHVzdWFsbHkgYW4gKHVubGltaXRlZCBub3dhZGF5cykgbnVtYmVyIG9mIHN1cHBsZW1lbnRhbCBmaWd1cmVzLg0KDQogICAgLSAgIE1haW4gZmlndXJlcyBjYW4gdXN1YWxseSBhY2NvbW9kYXRlIHVwIHRvIDQtOCBzdWJwYW5lbHMgZGVwZW5kaW5nIG9uIHRoZSBjb21wbGV4aXR5IG9mIHlvdXIgZGF0YS9zdWJqZWN0DQoNCi0gICAqKlByZXNlbnRhdGlvbnMqKiBnZW5lcmFsbHkgaGF2ZSAxIHNsaWRlIHBlciBtaW51dGUgb2YgdGFsayBzbyBwbGFuIGFjY29yZGluZ2x5Lg0KDQogICAgLSAgIEEgY29tcGxpY2F0ZWQgZmlndXJlIG9yIGZpZ3VyZSB0eXBlIHNob3VsZCBiZSBnaXZlbiBzb21lIGV4dHJhIHRpbWUgdG8gaW5pdGlhbGx5IGV4cGxhaW4gYnV0IGlmIHlvdSBhcmUgdXNpbmcgdGhlIGZvcm1hdCBvZnRlbiwgbGF0ZXIgdmVyc2lvbnMgd2lsbCBiZSBzaW1wbGVyIHRvIGRpZ2VzdC4gU3BlbmQgdGltZSBkaXNjdXNzaW5nIHRoZSBmaXJzdCBvY2N1cnJlbmNlIG9mIGEgZmlndXJlIHR5cGUgdG8gYnVpbGQgYSBzb2xpZCBmb3VuZGF0aW9uIQ0KDQojIyA0LjIuMCBNYW51c2NyaXB0IHNwZWNpZmljcw0KDQpXaGlsZSBwdWJsaWNhdGlvbiByZXF1aXJlbWVudHMgdmFyeSBmcm9tIGpvdXJuYWwgdG8gam91cm5hbCwgbW9zdCBoYXZlIHNpbWlsYXIgcnVsZXMNCg0KLSAgIE1pbmltdW0gZHBpIChyZXNvbHV0aW9uKSBvZiB5b3VyIGltYWdlczogMzAwZHBpDQoNCi0gICBNaW5pbXVtIGZvbnQgc2l6ZSAoaWUgdGhlIHZlcnkgc21hbGxlc3QgYW55IGZvbnQgc2hvdWxkIGJlIG9uIGEgc3ViIHBhbmVsKTogVXN1YWxseSA2LTdwdHMNCg0KLSAgIE1heGltdW0gaW1hZ2Ugc2l6ZS4gVGhlcmUgaXMgdXN1YWxseSBhIG1heGltdW0gZmlndXJlIHNpemUgaW4gdGVybXMgb2YgZGltZW5zaW9ucyBidXQgdGhpcyByZWFsbHkgY2FuIHZhcnkgd2lkZWx5LiBDaGVjayB3aXRoIGEgZmV3IHBvdGVudGlhbCBwdWJsaWNhdGlvbnMgZmlyc3QgdG8gZ2V0IGFuIGlkZWEgb2YgdGhlIG1haW4gc2l6ZS9sYXlvdXQgKHBvcnRyYWl0IHZzIGxhbmRzY2FwZSkgZm9yIGZpZ3VyZXMuDQoNCi0gICBLZWVwIEFMTCBmaWd1cmUgZGF0YSBvcmdhbml6ZWQgZm9yIHN1Ym1pc3Npb24hIFRoaXMgaXMgdGhlIHN0YW5kYXJkIG5vdyBhbmQgY2FuIGluY2x1ZGUgc2VxdWVuY2VzLCBvciBhbnkgZGF0YSB0aGF0IHdhcyB1c2VkIHRvIGNyZWF0ZSB5b3VyIGZpZ3VyZXMuIFlvdSBjYW4gb3JnYW5pemUgdGhpcyBpbiBhIHNpbmdsZSBDU1YsIG9yIHdvcmtzaGVldC1iYXNlZCBmaWxlLg0KDQotICAgRmlndXJlcyBzaG91bGQgYmUgZGVjaXBoZXJhYmxlIGF0IGEgZ2xhbmNlLiBBIHN1Y2Nlc3NmdWwgZmlndXJlJ3MgZ2VuZXJhbCBtZXNzYWdlIGNhbiBiZSB1bmRlcnN0b29kIHdpdGhvdXQgaGF2aW5nIHRvIHJlYWQgdGhyb3VnaCB0aGUgZmlndXJlIGxlZ2VuZHMuIFRoaXMgaXMgbm90ICphbHdheXMqIHBvc3NpYmxlIGJ1dCBpcyBjZXJ0YWlubHkgc29tZXRoaW5nIHRvIHN0cml2ZSBmb3IuDQoNCiAgICAtICAgRmlndXJlIHRpdGxlcyBhcmUgb3B0aW9uYWwuIE9mdGVuIGhlbHBmdWwgZm9yIHRoZSBhYm92ZSBwb2ludCBidXQgeW91IHNhY3JpZmljZSBzcGFjZSB0byBoYXZlIHRoZW0hDQoNCiMjIDQuMy4wIFByZXNlbnRhdGlvbiBmaWd1cmVzDQoNCi0gICBNYWtlIHlvdXIgZm9udCBiaWcgZW5vdWdoIHRvIHJlYWQuIFVubGlrZSBhIG1hbnVzY3JpcHQsIHBlb3BsZSBjYW5ub3Qgem9vbSBpbiBvbiB5b3VyIHByZXNlbnRhdGlvbiBmaWd1cmVzDQoNCi0gICBMaW1pdCBwYW5lbHMgdG8gMi00IGF0IG1vc3QgcGVyIGZpZ3VyZS4NCg0KLSAgIEZvcmdldCBhYm91dCB0aGUgdGl0bGUgb24geW91ciBmaWd1cmUuIFRpdGxlIHRoZSAqKipzbGlkZSoqKiBpbnN0ZWFkIHdpdGggeW91ciB0YWtlLWhvbWUgbWVzc2FnZSBhYm91dCB0aGUgZmlndXJlLiBIYXZpbmcgYW4gZWZmZWN0aXZlIHNsaWRlIHRpdGxlIHRlbGxzIHlvdXIgYXVkaWVuY2Ugd2hlcmUgdG8gZm9jdXMgb24geW91ciBmaWd1cmUuIFRoaXMgaXMgKmVzcGVjaWFsbHkga2V5KiB3aGVuIHlvdSBhcmUgcHJlc2VudGluZyBhIGNvbXBsaWNhdGVkIG9yICJidXN5IiBmaWd1cmUuDQoNCi0gICBXaGVuIHByZXNlbnRpbmcgYSBmaWd1cmUgd2l0aCBtdWx0aXBsZSBjYXRlZ29yaWVzIG9yIGlkZWFzLCByZXZlYWwgaXQgcGllY2VtZWFsLiBQcmVzZW50IHRoZSBiYXNlIGlkZWEgKGllIGEgY29udHJvbCkgYW5kIHRoZW4gcmV2ZWFsIHRoZSBuZXh0IHBhcnQgb2YgdGhlIHBhbmVsIHNob3dpbmcgeW91ciBmaXJzdCBleHBlcmltZW50YWwgY29uZGl0aW9uLCB0aGVuIHlvdXIgc2Vjb25kIGV0Yy4gVGhpcyBhbGxvd3MgdGhlIGF1ZGllbmNlIHRvIGFjY2xpbWF0ZSB0byB0aGUgaWRlYSB5b3Ugd2lzaCB0byBjb252ZXkuDQoNCi0gICBJZiB5b3UgY2FuJ3QgcmVhZCB0aGUgdGV4dCBvZiBhIGZpZ3VyZSBiZWNhdXNlIHRoZXJlIGFyZSBqdXN0IHRvbyBtYW55IHRoaW5ncywgY29uc2lkZXIgc2ltcGxpZnlpbmcgb3IgcmVtb3ZpbmcgdGhlIHRleHQuIFRoaXMgd2lsbCByZWR1Y2Ugb24gZGlzdHJhY3RpbmcgaW1hZ2VyeSBhbmQgZm9jdXMgeW91ciBtZXNzYWdlIHRvIHlvdXIgYXVkaWVuY2UuDQoNCiMjIDQuNC4wIFRoaW5ncyB0byB0aGluayBhYm91dCB3aGVuIGNvZGluZw0KDQotICAgS2VlcCBhIHNjcmlwdCAob3Igbm90ZWJvb2shKSBmb3IgcmVtYWtpbmcgYWxsIHlvdXIgZmlndXJlcyENCg0KICAgIC0gICBTaW1wbGlmeSB3aXRoIGEgZmV3IHZhcmlhYmxlcyB0aGluZ3MgbGlrZSB0aGUgbG9jYXRpb24gb2YgeW91ciBkYXRhc2V0cyENCg0KICAgIC0gICBCdWlsZCBmdW5jdGlvbnMgdG8gbWFrZSBmaWd1cmVzIG9mIHRoZSBzYW1lIHR5cGUgd2l0aCBkaWZmZXJlbnQgZGF0YXNldHMuDQoNCiAgICAtICAgQ3JlYXRlIGEgbWFzdGVyIGZpbGUvdGFibGUgdG8gaW1wb3J0IGFuZCB1c2UgdGhlc2UgZGF0YXNldHMgYW5kIGZ1bmN0aW9ucy4NCg0KLSAgIENyZWF0ZSBhIHNwZWNpZmljIGNvbG91ci1zY2hlbWUgZm9yIHlvdXIgZmlndXJlcw0KDQogICAgLSAgIFRyeSB0byB1c2UgdGhlIHNhbWUgY29sb3VyIGZvciBjb250cm9scyB2ZXJzdXMgc3BlY2lmaWMgbXV0YW50IGdlbm90eXBlcyB5b3UgbWF5IGJlIGV4YW1pbmluZyBvciByZS11c2luZyBmcmVxdWVudGx5DQoNCiAgICAtICAgVXNlIGEgY29sb3VyYmxpbmQtZnJpZW5kbHkgcGFsZXR0ZQ0KDQotICAgU2F2ZSB5b3VyIHBsb3RzIGFzIFNWRywgUE5HIG9yIFRJRkZzDQoNCiAgICAtICAgU1ZHcyBhcmUgdmVjdG9yaXplZCBtZWFuaW5nIHRoZXkgYXJlIGZpbGVzIHJlcHJlc2VudGluZyBsaW5lcywgY3VydmVzLCBhbmQgc2hhcGVzIGJhc2VkIG9uIGEgY29vcmRpbmF0ZSBzeXN0ZW0uIFRoaXMgbWVhbnMgdGhleSBhcmUgZ2VuZXJhbGx5IHJlc29sdXRpb25sZXNzIGFuZCBjYW4gYmUgYmxvd24gdXAgdG8gYW55IHNpemUgYXMgbmVlZGVkLiBHcmVhdCBmb3Igd29ya2luZyB3aXRoIG9uIHByZXNlbnRhdGlvbnMgYW5kIHNpbXBsZSB0byBhbHRlciBpbiBtb3N0IGltYWdlIGVkaXRpbmcgcHJvZ3JhbXMuIElkZWFsIGZvciB1c2luZyBvbiBncmFwaHMgYW5kIGdncGxvdCBmaWd1cmVzIGJ1dCBub3QgbWljcm9zY29weSBvciBvdGhlciBkaWdpdGFsIGltYWdlcy4NCg0KICAgIC0gICBBdm9pZCBKUEVHcyBhcyBtdWNoIGFzIHBvc3NpYmxlLiBUaGUgdGVuZCB0byBoYXZlIHBvb3IgKGxvc3N5KSBlbmNvZGluZyB0aGF0IGNhbiBjcmVhdGUgcmVzb2x1dGlvbiBsb3NzIHVubGVzcyB5b3Ugc2F2ZSB0aGVtIGFzIGJpZyBoaWdoLXF1YWxpdHkgSlBFRyB2ZXJzaW9ucyAobG9zc2xlc3MpLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyA1LjAuMCBDbGFzcyBTdW1tYXJ5DQoNCldpdGggdGhpcyBmaW5hbCBsZWN0dXJlIHdlJ3ZlIGNvdmVyZWQgdGhlIHNwZWN0cnVtIG9mIHZpc3VhbGl6YXRpb25zIGNvdmVyaW5nIHRoZSBtb3N0IGJhc2ljIG9mIHNjYXR0ZXItIGFuZCBiYXJwbG90cywgZ3JhZHVhdGluZyB0byBib3hwbG90cywgdmlvbGluIHBsb3RzLCBhbmQgdGhlaXIgdmFyaWFudHMuIFdlJ3ZlIGNvdmVyZWQgaGlnaC10aHJvdWdocHV0IGRhdGEgdmlzdWFsaXphdGlvbnMgaW5jbHVkaW5nIHZvbGNhbm8gcGxvdHMsIGhlYXRtYXBzLCBhbmQgcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcy4gRnVydGhlcm1vcmUgd2UgbG9va2VkIGF0IGhvdyBzaW1wbGUgdGhlIHByb2plY3Rpb24gb2YgaGlnaC1kaW1lbnNpb24gZGF0YSBjYW4gYmUgd2l0aCB0LVNORSBhbmQgVU1BUC4NCg0KV2UgZmluaXNoZWQgb3VyIGNvdXJzZSB0b2RheSBjb3ZlcmluZyBwaHlsb2dlbmV0aWMgdHJlZXMsIG5ldHdvcmsgZ3JhcGhzLCBhbmQgc29tZSBzZXF1ZW5jZSBhbmFseXNpcyB2aXN1YWxpemF0aW9ucy4gT3ZlcmFsbCB5b3Ugbm93IGhhdmUgdGhlIHRvb2xzIHRvIHdyYW5nbGUgZGF0YSB0aGF0IG1heSBhcHBlYXIgaW4gYWxsIHNvcnRzIG9mIGZvcm1hdHMgYWxvbmcgd2l0aCBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mIHdoZW4gYW5kIGhvdyB0byBwcmVwYXJlIHNvbWUgb2YgdGhlIG1vc3QgY29tbW9uIGRhdGEgdmlzdWFsaXphdGlvbnMgaW4geW91ciBidXJnZW9uaW5nIHNjaWVuY2UgY2FyZWVycy4NCg0KQ29uZ3JhdHVsYXRpb25zIGFuZCBnb29kIGx1Y2sgb24geW91ciBkYXRhIHNjaWVuY2Ugam91cm5leSENCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA1LjAuMSBQb3N0LWNvdXJzZSBzdXJ2ZXkNCg0KV2UgaGF2ZSBjcmVhdGVkIGEgcG9zdC1jb3Vyc2Ugc3VydmV5IHlvdSBjYW4gZmlsbCBvdXQgYW5vbnltb3VzbHkuIFlvdSBjYW4gdXNlIHRoaXMgc3VydmV5IGFzIGFuIG9wcG9ydHVuaXR5IHRvIHRlbGwgdXMgYWJvdXQgeW91ciBleHBlcmllbmNlIGFuZCBoZWxwIHNoYXBlIHRoZSBmdXR1cmUgb2ZmZXJpbmdzIG9mIHRoaXMgc2VyaWVzLiBQbGVhc2UgdGFrZSA1LTEwIG1pbnV0ZXMgdG8gZmlsbCBvdXQgdGhlIHN1cnZleS4gV2UgcmVhbGx5IGFwcHJlY2lhdGUgeW91ciBmZWVkYmFjayENCg0KW0Fub255bW91cyBHb29nbGUgU3VydmV5IGZvdW5kIGhlcmVdKGh0dHBzOi8vZm9ybXMuZ2xlL0FhWWt5cnd0dVhUeWFhVUZBKQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNS4xLjAgV2Vla2x5IGFzc2lnbm1lbnQNCg0KVGhpcyB3ZWVrJ3MgYXNzaWdubWVudCB3aWxsIGJlIGZvdW5kIHVuZGVyIHRoZSBjdXJyZW50IGxlY3R1cmUgZm9sZGVyIHVuZGVyIHRoZSAiYXNzaWdubWVudCIgc3ViZm9sZGVyLiBJdCB3aWxsIGluY2x1ZGUgYW4gUiBtYXJrZG93biBub3RlYm9vayB0aGF0IHlvdSB3aWxsIHVzZSB0byBwcm9kdWNlIHRoZSBjb2RlIGFuZCBhbnN3ZXJzIGZvciB0aGlzIHdlZWsncyBhc3NpZ25tZW50LiBQbGVhc2UgcHJvdmlkZSBhbnN3ZXJzIGluIG1hcmtkb3duIG9yIGNvZGUgY2VsbHMgdGhhdCBpbW1lZGlhdGVseSBmb2xsb3cgZWFjaCBxdWVzdGlvbiBzZWN0aW9uLg0KDQp8ICAgICAgICAgICAgICAgICAgICB8IEFzc2lnbm1lbnQgYnJlYWtkb3duIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgICAgICAgIENvZGUgICAgICAgIHwgICAgICAgICA1MCUgICAgICAgICAgfCBcLSBEb2VzIGl0IGZvbGxvdyBiZXN0IHByYWN0aWNlcz8gICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gRG9lcyBpdCBtYWtlIGdvb2QgdXNlIG9mIGF2YWlsYWJsZSBwYWNrYWdlcz8gfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIFdhcyBkYXRhIHByZXBhcmVkIHByb3Blcmx5ICAgICAgICAgICAgICAgICAgIHwNCnwgQW5zd2VycyBhbmQgT3V0cHV0IHwgICAgICAgICA1MCUgICAgICAgICAgfCBcLSBJcyBvdXRwdXQgYmFzZWQgb24gdGhlIGNvcnJlY3QgZGF0YXNldD8gICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gQXJlIGdyb3VwaW5ncyBhcHByb3ByaWF0ZSAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIEFyZSBjb3JyZWN0IHRpdGxlcy9heGVzL2xlZ2VuZHMgY29ycmVjdD8gICAgIHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBJcyBpbnRlcnByZXRhdGlvbiBvZiB0aGUgZ3JhcGhzIGNvcnJlY3Q/ICAgICB8DQoNClNpbmNlIGNvZGluZyBzdHlsZXMgYW5kIHNvbHV0aW9ucyBjYW4gZGlmZmVyLCBzdHVkZW50cyBhcmUgZW5jb3VyYWdlZCB0byB1c2UgYmVzdCBwcmFjdGljZXMuIEFzc2lnbm1lbnRzICptYXkqIGJlIHJld2FyZGVkIGZvciB3ZWxsLWNvZGVkIG9yIGVsZWdhbnQgc29sdXRpb25zLg0KDQpZb3UgY2FuIHNhdmUgYW5kIGRvd25sb2FkIHRoZSBtYXJrZG93biBub3RlYm9vayBpbiBpdHMgbmF0aXZlIGZvcm1hdC4gU3VibWl0IHRoaXMgZmlsZSB0byB0aGUgdGhlIGFwcHJvcHJpYXRlIGFzc2lnbm1lbnQgc2VjdGlvbiBieSAxMjo1OSBwbSBvbiB0aGUgZGF0ZSBvZiBvdXIgbmV4dCBjbGFzczogQXByaWwgMjV0aCwgMjAyNC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDUuMi4wIEFja25vd2xlZGdlbWVudHMNCg0KKipSZXZpc2lvbiAxLjAuMCoqOiBjcmVhdGVkIGFuZCBwcmVwYXJlZCBmb3IgKipDU0IxMDIxSCBTIExFQzAxNDEqKiwgMDMtMjAyMSBieSBDYWx2aW4gTW9rLCBQaC5ELiAqQmlvaW5mb3JtYXRpY2lhbiwgRWR1Y2F0aW9uIGFuZCBPdXRyZWFjaCwgQ0FHRUYuKg0KDQoqKlJldmlzaW9uIDEuMC4xKio6IGVkaXRlZCBhbmQgcHJlcGFyZWQgZm9yICoqQ1NCMTAyMEggUyBMRUMwMTQxKiosIDAzLTIwMjIgYnkgQ2FsdmluIE1vaywgUGguRC4gKkJpb2luZm9ybWF0aWNpYW4sIEVkdWNhdGlvbiBhbmQgT3V0cmVhY2gsIENBR0VGLioNCg0KKipSZXZpc2lvbiAxLjAuMioqOiBlZGl0ZWQgYW5kIHByZXBhcmVkIGZvciAqKkNTQjEwMjBIIFMgTEVDMDE0MSoqLCAwMy0yMDIzIGJ5IENhbHZpbiBNb2ssIFBoLkQuICpCaW9pbmZvcm1hdGljaWFuLCBFZHVjYXRpb24gYW5kIE91dHJlYWNoLCBDQUdFRi4qDQoNCioqUmV2aXNpb24gMi4wLjAqKjogUmV2aXNlZCBhbmQgcHJlcGFyZWQgZm9yICoqQ1NCMTAyMEggUyBMRUMwMTQxKiosIDAzLTIwMjQgYnkgQ2FsdmluIE1vaywgUGguRC4gKkJpb2luZm9ybWF0aWNpYW4sIEVkdWNhdGlvbiBhbmQgT3V0cmVhY2gsIENBR0VGLioNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDUuMy4wIFJlZmVyZW5jZXMNCg0KVGhlIGJvb2sgb2YgYGdndHJlZWAgZnJvbSB0aGUgWXUgTGFiOiA8aHR0cHM6Ly95dWxhYi1zbXUudG9wL3RyZWVkYXRhLWJvb2svaW5kZXguaHRtbD4NCg0KQ2hhcHRlciAxMCBvZiBgZ2d0cmVlYCB3aXRoIGFtYXppbmcgYW5kIGNvbXBsaWNhdGVkIHBsb3RzOiA8aHR0cHM6Ly95dWxhYi1zbXUudG9wL3RyZWVkYXRhLWJvb2svY2hhcHRlcjEwLmh0bWw+DQoNCk1vcmUgaW5mb3JtYXRpb24gb24gdGhlIGBnZ3JhcGhgIHBhY2thZ2U6IDxodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dyYXBoL2dncmFwaC5wZGY+DQoNCmBnZ3NlcWxvZ29gIHBhY2thZ2UgaW5mb3JtYXRpb246IDxodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dzZXFsb2dvL2dnc2VxbG9nby5wZGY+DQoNCk1hbmhhdHRhbiBwbG90IHR1dG9yaWFsOiA8aHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS8xMDFfTWFuaGF0dGFuX3Bsb3QuaHRtbD4NCg0KR1dBUyBwLXZhbHVlIHRocmVzaG9sZDogPGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxMzgwLTAyMC0wNjcwLTM/cHJvb2Y9dD4NCg0KTW9yZSBvbiBHV0FTIHRocmVzaG9sZHMgZm9yIHNpZ25pZmljYW5jZTogPGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvZWpoZzIwMTUyNjkucGRmPg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgVGhlIENlbnRlciBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gYW5kIEZ1bmN0aW9uIChDQUdFRikNCg0KVGhlIENlbnRyZSBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gYW5kIEZ1bmN0aW9uIChDQUdFRikgYXQgdGhlIFVuaXZlcnNpdHkgb2YgVG9yb250byBvZmZlcnMgY29tcHJlaGVuc2l2ZSBleHBlcmltZW50YWwgZGVzaWduLCByZXNlYXJjaCwgYW5kIGFuYWx5c2lzIHNlcnZpY2VzIGluIG1pY3JvYmlvbWUgYW5kIG1ldGFnZW5vbWljIHN0dWRpZXMsIGdlbm9taWNzLCBwcm90ZW9taWNzLCBhbmQgYmlvaW5mb3JtYXRpY3MuDQoNCkZyb20gdGFyZ2V0ZWQgRE5BIGFtcGxpY29uIHNlcXVlbmNpbmcgdG8gdHJhbnNjcmlwdG9tZXMsIHdob2xlIGdlbm9tZXMsIGFuZCBtZXRhZ2Vub21lcywgZnJvbSBwcm90ZWluIGlkZW50aWZpY2F0aW9uIHRvIHBvc3QtdHJhbnNsYXRpb25hbCBtb2RpZmljYXRpb24sIENBR0VGIGhhcyB0aGUgdG9vbHMgYW5kIGtub3dsZWRnZSB0byBzdXBwb3J0IHlvdXIgcmVzZWFyY2guIE91ciBzdGF0ZS1vZi10aGUtYXJ0IGZhY2lsaXR5IGFuZCBleHBlcmllbmNlZCByZXNlYXJjaCBzdGFmZiBwcm92aWRlIGEgYnJvYWQgcmFuZ2Ugb2Ygc2VydmljZXMsIGluY2x1ZGluZyBib3RoIHN0YW5kYXJkIGFuYWx5c2VzIGFuZCB0ZWNobmlxdWVzIGRldmVsb3BlZCBieSBvdXIgdGVhbS4gSW4gcGFydGljdWxhciwgd2UgaGF2ZSBzcGVjaWFsIGV4cGVydGlzZSBpbiBtaWNyb2JpYWwsIHBsYW50LCBhbmQgZW52aXJvbm1lbnRhbCBzeXN0ZW1zLg0KDQpGb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB1cyBhbmQgdGhlIHNlcnZpY2VzIHdlIG9mZmVyLCBwbGVhc2UgdmlzaXQgPGh0dHBzOi8vd3d3LmNhZ2VmLnV0b3JvbnRvLmNhLz4uDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovQ0FHRUZfbmV3LnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQo=